From 78a7a0260e442f0365ce9b335a7db46bfa8b6f21 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 13:01:26 -0500 Subject: [PATCH 01/91] check --- bench/Chainweb/Pact/Backend/Bench.hs | 4 +- bench/Chainweb/Pact/Backend/ForkingBench.hs | 29 +- bench/JSONEncoding.hs | 1 + cabal.project | 2 + chainweb.cabal | 19 +- docs/RecoveringFromDeepForks.md | 2 +- node/ChainwebNode.hs | 17 +- src/Chainweb/BlockHeader.hs | 307 +++- src/Chainweb/BlockHeader/Genesis.hs | 356 ---- src/Chainweb/BlockHeader/Validation.hs | 25 +- src/Chainweb/BlockHeaderDB/Internal.hs | 5 +- src/Chainweb/BlockHeaderDB/PruneForks.hs | 4 +- src/Chainweb/BlockHeaderDB/RestAPI/Client.hs | 25 +- src/Chainweb/BlockHeaderDB/RestAPI/Server.hs | 2 +- src/Chainweb/BlockHeight.hs | 4 +- src/Chainweb/ChainId.hs | 46 +- src/Chainweb/Chainweb.hs | 22 +- src/Chainweb/Chainweb/ChainResources.hs | 13 +- src/Chainweb/Chainweb/Configuration.hs | 105 +- src/Chainweb/Chainweb/MinerResources.hs | 4 +- src/Chainweb/Chainweb/PeerResources.hs | 4 +- src/Chainweb/Crypto/MerkleLog.hs | 3 - src/Chainweb/Cut.hs | 1 - src/Chainweb/Cut/Create.hs | 2 + src/Chainweb/Cut/CutHashes.hs | 5 +- src/Chainweb/CutDB.hs | 2 +- src/Chainweb/Difficulty.hs | 49 +- src/Chainweb/Graph.hs | 2 +- src/Chainweb/Mempool/Consensus.hs | 22 +- src/Chainweb/Mempool/Mempool.hs | 5 +- src/Chainweb/Miner/Core.hs | 16 - src/Chainweb/Miner/Miners.hs | 14 +- src/Chainweb/Miner/RestAPI/Server.hs | 1 + src/Chainweb/NodeVersion.hs | 8 +- src/Chainweb/Pact/Backend/ChainwebPactDb.hs | 7 +- .../Pact/Backend/RelationalCheckpointer.hs | 7 +- src/Chainweb/Pact/Backend/Utils.hs | 2 +- src/Chainweb/Pact/PactService.hs | 36 +- src/Chainweb/Pact/PactService/ExecBlock.hs | 42 +- src/Chainweb/Pact/SPV.hs | 4 +- src/Chainweb/Pact/Service/PactInProcApi.hs | 2 +- src/Chainweb/Pact/Service/Types.hs | 2 +- src/Chainweb/Pact/TransactionExec.hs | 254 ++- .../Pact/Transactions/CoinV3Transactions.hs | 7 +- .../Pact/Transactions/CoinV4Transactions.hs | 7 +- .../Pact/Transactions/CoinV5Transactions.hs | 7 +- .../Transactions/DevelopmentTransactions.hs | 7 +- .../Pact/Transactions/Mainnet0Transactions.hs | 7 +- .../Pact/Transactions/Mainnet1Transactions.hs | 7 +- .../Pact/Transactions/Mainnet2Transactions.hs | 7 +- .../Pact/Transactions/Mainnet3Transactions.hs | 7 +- .../Pact/Transactions/Mainnet4Transactions.hs | 7 +- .../Pact/Transactions/Mainnet5Transactions.hs | 7 +- .../Pact/Transactions/Mainnet6Transactions.hs | 7 +- .../Pact/Transactions/Mainnet7Transactions.hs | 7 +- .../Pact/Transactions/Mainnet8Transactions.hs | 7 +- .../Pact/Transactions/Mainnet9Transactions.hs | 7 +- .../Transactions/MainnetKADTransactions.hs | 7 +- .../Pact/Transactions/OtherTransactions.hs | 7 +- .../Pact/Transactions/UpgradeTransactions.hs | 8 +- src/Chainweb/Pact/Types.hs | 9 +- src/Chainweb/Payload/PayloadStore.hs | 20 +- src/Chainweb/Payload/RestAPI/Server.hs | 2 +- src/Chainweb/PowHash.hs | 13 +- src/Chainweb/RestAPI/NodeInfo.hs | 11 +- src/Chainweb/RestAPI/Orphans.hs | 4 +- src/Chainweb/Rosetta/Internal.hs | 34 +- src/Chainweb/Rosetta/RestAPI.hs | 6 +- src/Chainweb/Rosetta/RestAPI/Client.hs | 2 +- src/Chainweb/Rosetta/RestAPI/Server.hs | 17 +- src/Chainweb/Rosetta/Utils.hs | 15 +- src/Chainweb/Sync/WebBlockHeaderStore.hs | 15 +- src/Chainweb/Transaction.hs | 31 +- src/Chainweb/TreeDB/RemoteDB.hs | 3 +- src/Chainweb/Utils.hs | 5 + src/Chainweb/Utils/Rule.hs | 94 ++ src/Chainweb/Version.hs | 1441 ++++++----------- src/Chainweb/Version/Codes.hs | 3 + src/Chainweb/Version/Development.hs | 100 ++ src/Chainweb/Version/Guards.hs | 119 ++ src/Chainweb/Version/Mainnet.hs | 196 +++ src/Chainweb/Version/Registry.hs | 69 + src/Chainweb/Version/Testnet.hs | 166 ++ src/Chainweb/Version/Utils.hs | 40 +- src/Chainweb/WebBlockHeaderDB.hs | 4 +- src/Chainweb/WebPactExecutionService.hs | 2 +- src/P2P/Node.hs | 20 +- src/P2P/Node/PeerDB.hs | 50 +- src/P2P/Node/RestAPI/Server.hs | 2 +- src/P2P/Peer.hs | 50 - test/Chainweb/Test/BlockHeader/Genesis.hs | 33 +- test/Chainweb/Test/BlockHeader/Validation.hs | 6 +- .../Chainweb/Test/BlockHeaderDB/PruneForks.hs | 1 + test/Chainweb/Test/Cut.hs | 5 +- test/Chainweb/Test/CutDB.hs | 9 +- test/Chainweb/Test/Mempool/Consensus.hs | 3 +- test/Chainweb/Test/Mempool/InMem.hs | 4 +- test/Chainweb/Test/Mempool/RestAPI.hs | 5 +- test/Chainweb/Test/Mempool/Sync.hs | 6 +- test/Chainweb/Test/Mining.hs | 16 +- test/Chainweb/Test/MultiNode.hs | 10 +- test/Chainweb/Test/Orphans/Internal.hs | 41 +- .../Chainweb/Test/P2P/Peer/BootstrapConfig.hs | 50 +- test/Chainweb/Test/Pact/Checkpointer.hs | 6 +- .../Test/Pact/ModuleCacheOnRestart.hs | 9 +- test/Chainweb/Test/Pact/PactExec.hs | 7 +- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 19 +- test/Chainweb/Test/Pact/PactReplay.hs | 5 +- .../Chainweb/Test/Pact/PactSingleChainTest.hs | 6 +- test/Chainweb/Test/Pact/RemotePactTest.hs | 165 +- test/Chainweb/Test/Pact/RewardsTest.hs | 4 +- test/Chainweb/Test/Pact/SPV.hs | 14 +- test/Chainweb/Test/Pact/TTL.hs | 4 +- test/Chainweb/Test/Pact/TransactionTests.hs | 28 +- test/Chainweb/Test/Pact/Utils.hs | 19 +- test/Chainweb/Test/RestAPI.hs | 39 +- test/Chainweb/Test/RestAPI/Client_.hs | 18 +- test/Chainweb/Test/RestAPI/Utils.hs | 21 +- test/Chainweb/Test/Rosetta.hs | 2 + test/Chainweb/Test/Rosetta/RestAPI.hs | 82 +- test/Chainweb/Test/Roundtrips.hs | 13 +- test/Chainweb/Test/SPV.hs | 21 +- test/Chainweb/Test/TestVersions.hs | 269 +++ test/Chainweb/Test/Utils.hs | 131 +- test/Chainweb/Test/Utils/ApiQueries.hs | 12 +- test/Chainweb/Test/Utils/BlockHeader.hs | 1 - test/Chainweb/Test/Utils/TestHeader.hs | 107 +- test/Chainweb/Test/Version.hs | 36 +- test/SlowTests.hs | 5 +- .../development-block-hashes-expected.txt | 2 +- test/golden/devnet-block-hashes-expected.txt | 1 + test/golden/empty-block-tests-expected.txt | 6 +- test/golden/new-block-0-expected.txt | 8 +- tools/cwtool/TxSimulator.hs | 8 +- tools/ea/Ea.hs | 10 +- tools/ea/Ea/Genesis.hs | 6 +- tools/genconf/GenConf.hs | 1 + tools/header-dump/HeaderDump.hs | 11 +- tools/run-nodes/RunNodes.hs | 12 +- tools/txstream/TxStream.hs | 30 +- 140 files changed, 2912 insertions(+), 2560 deletions(-) delete mode 100644 src/Chainweb/BlockHeader/Genesis.hs create mode 100644 src/Chainweb/Utils/Rule.hs create mode 100644 src/Chainweb/Version/Codes.hs create mode 100644 src/Chainweb/Version/Development.hs create mode 100644 src/Chainweb/Version/Guards.hs create mode 100644 src/Chainweb/Version/Mainnet.hs create mode 100644 src/Chainweb/Version/Registry.hs create mode 100644 src/Chainweb/Version/Testnet.hs create mode 100644 test/Chainweb/Test/TestVersions.hs create mode 100644 test/golden/devnet-block-hashes-expected.txt diff --git a/bench/Chainweb/Pact/Backend/Bench.hs b/bench/Chainweb/Pact/Backend/Bench.hs index 54c78d04a7..b7301cea25 100644 --- a/bench/Chainweb/Pact/Backend/Bench.hs +++ b/bench/Chainweb/Pact/Backend/Bench.hs @@ -35,6 +35,7 @@ import qualified Pact.Types.SQLite as PSQL -- chainweb imports import Chainweb.BlockHash +import Chainweb.BlockHeader import Chainweb.BlockHeight import Chainweb.Graph import Chainweb.MerkleLogHash @@ -42,12 +43,13 @@ import Chainweb.Pact.Backend.RelationalCheckpointer import Chainweb.Pact.Backend.Types import Chainweb.Pact.Backend.Utils import Chainweb.Pact.Types +import Chainweb.Test.TestVersions import Chainweb.Utils.Bench import Chainweb.Utils (sshow) import Chainweb.Version v :: ChainwebVersion -v = FastTimedCPM petersonChainGraph +v = fastForkingCpmTestVersion petersonChainGraph bench :: C.Benchmark bench = C.bgroup "pact-backend" $ diff --git a/bench/Chainweb/Pact/Backend/ForkingBench.hs b/bench/Chainweb/Pact/Backend/ForkingBench.hs index ef5b7b4fd9..905b2f381f 100644 --- a/bench/Chainweb/Pact/Backend/ForkingBench.hs +++ b/bench/Chainweb/Pact/Backend/ForkingBench.hs @@ -71,7 +71,6 @@ import Pact.Types.Util hiding (unwrap) import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeaderDB import Chainweb.BlockHeaderDB.Internal import Chainweb.ChainId @@ -89,6 +88,7 @@ import Chainweb.Pact.Utils (toTxCreationTime) import Chainweb.Payload import Chainweb.Payload.PayloadStore import Chainweb.Payload.PayloadStore.InMemory +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils @@ -379,7 +379,7 @@ cid :: ChainId cid = someChainId testVer testVer :: ChainwebVersion -testVer = FastTimedCPM petersonChainGraph +testVer = fastForkingCpmTestVersion petersonChainGraph assertNotLeft :: (MonadThrow m, Exception e) => Either e a -> m a assertNotLeft (Left l) = throwM l @@ -397,7 +397,10 @@ createCoinAccount v meta name = do nameKeyset <- NEL.fromList <$> getKeyset name let attach = attachCaps "sender00" name 1000.0 let theData = object [T.pack name .= fmap (formatB16PubKey . fst) (attach nameKeyset)] - res <- mkExec (T.pack theCode) theData meta (NEL.toList $ attach sender00Keyset) (Just $ Pact.NetworkId $ toText v) Nothing + res <- mkExec (T.pack theCode) theData meta + (NEL.toList $ attach sender00Keyset) + (Just $ Pact.NetworkId $ toText (_versionName v)) + Nothing pure (nameKeyset, res) where theCode = printf "(coin.transfer-create \"sender00\" \"%s\" (read-keyset \"%s\") 1000.0)" name name @@ -550,14 +553,20 @@ createCoinContractRequest v meta ks request = object [ "create-account-guard" .= fmap (formatB16PubKey . fst) guardd ] - mkExec (T.pack theCode) theData meta (NEL.toList ks) (Just $ Pact.NetworkId $ toText v) Nothing + mkExec (T.pack theCode) theData meta + (NEL.toList ks) + (Just $ Pact.NetworkId $ toText $ _versionName v) + Nothing CoinAccountBalance (Account account) -> do let theData = Null theCode = printf "(coin.get-balance \"%s\")" account - mkExec (T.pack theCode) theData meta (NEL.toList ks) (Just $ Pact.NetworkId $ toText v) Nothing + mkExec (T.pack theCode) theData meta + (NEL.toList ks) + (Just $ Pact.NetworkId $ toText $ _versionName v) + Nothing CoinTransferAndCreate (SenderName (Account sn)) (ReceiverName (Account rn)) (Guard guardd) (Amount amount) -> do let theCode = printf @@ -570,7 +579,10 @@ createCoinContractRequest v meta ks request = object [ "receiver-guard" .= fmap (formatB16PubKey . fst) guardd ] - mkExec (T.pack theCode) theData meta (NEL.toList ks) (Just $ Pact.NetworkId $ toText v) Nothing + mkExec (T.pack theCode) theData meta + (NEL.toList ks) + (Just $ Pact.NetworkId $ toText $ _versionName v) + Nothing CoinTransfer (SenderName (Account sn)) (ReceiverName (Account rn)) (Amount amount) -> do let theCode = @@ -581,7 +593,10 @@ createCoinContractRequest v meta ks request = -- Super janky, but gets the job done for now (fromRational @Double $ toRational amount) theData = object [] - mkExec (T.pack theCode) theData meta (NEL.toList ks) (Just $ Pact.NetworkId $ toText v) Nothing + mkExec (T.pack theCode) theData meta + (NEL.toList ks) + (Just $ Pact.NetworkId $ toText $ _versionName v) + Nothing makeMetaWithSender :: String -> ChainId -> IO PublicMeta makeMetaWithSender sender c = diff --git a/bench/JSONEncoding.hs b/bench/JSONEncoding.hs index fb24477059..c3f38c9987 100644 --- a/bench/JSONEncoding.hs +++ b/bench/JSONEncoding.hs @@ -45,6 +45,7 @@ import Chainweb.RestAPI.NodeInfo import Chainweb.Test.Orphans.Internal import Chainweb.Utils.Paging import Chainweb.Version +import Chainweb.Version.Mainnet -- -------------------------------------------------------------------------- -- -- Main diff --git a/cabal.project b/cabal.project index ecf2dd208a..4b14a4c7f2 100644 --- a/cabal.project +++ b/cabal.project @@ -113,3 +113,5 @@ constraints: megaparsec <9.3 -- TODO remove once the bounds are upgraded in pact. allow-newer: pact:direct-sqlite +-- TODO remove once we update the freeze file +constraints: resource-pool < 0.4 diff --git a/chainweb.cabal b/chainweb.cabal index e7ff955f76..d4142d415c 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -121,7 +121,6 @@ library , Chainweb.BlockCreationTime , Chainweb.BlockHash , Chainweb.BlockHeader - , Chainweb.BlockHeader.Genesis , Chainweb.BlockHeader.Genesis.Development0Payload , Chainweb.BlockHeader.Genesis.DevelopmentKADPayload , Chainweb.BlockHeader.Genesis.DevelopmentNPayload @@ -234,8 +233,14 @@ library , Chainweb.Utils , Chainweb.Utils.Paging , Chainweb.Utils.RequestLog + , Chainweb.Utils.Rule , Chainweb.Utils.Serialization , Chainweb.Version + , Chainweb.Version.Development + , Chainweb.Version.Guards + , Chainweb.Version.Mainnet + , Chainweb.Version.Registry + , Chainweb.Version.Testnet , Chainweb.Version.Utils , Chainweb.WebBlockHeaderDB , Chainweb.WebPactExecutionService @@ -305,7 +310,7 @@ library , Chainweb.Pact.Transactions.Mainnet9Transactions , Chainweb.Pact.Transactions.MainnetKADTransactions , Chainweb.Pact.Transactions.OtherTransactions - , Chainweb.Pact.Transactions.UpgradeTransactions + -- , Chainweb.Pact.Transactions.UpgradeTransactions , Chainweb.Pact.Types , Chainweb.Pact.Utils @@ -317,6 +322,7 @@ library build-depends: Decimal >= 0.4.2 + , adjunctions >= 4.4.2 , aeson >= 1.4.3 , asn1-encoding >=0.9 , asn1-types >=0.3 @@ -343,6 +349,7 @@ library , digraph >= 0.2 , direct-sqlite >= 2.3.27 , directory >= 1.3 + , distributive >= 0.6.2.1 , dlist >= 0.8 , errors >= 2.3 , ethereum >= 0.1 @@ -373,10 +380,12 @@ library , pact >= 4.2.0.1 , pem >=0.2 , primitive >= 0.7.1.0 + , pretty-show , random >= 1.2 , rosetta >= 1.0 , safe-exceptions >= 0.1 , scheduler >= 1.4 + , semigroupoids >= 5.3.7 , servant >= 0.18.2 , servant-client >= 0.18.2 , servant-client-core >= 0.18.2 @@ -478,13 +487,14 @@ test-suite chainweb-tests Chainweb.Test.SPV Chainweb.Test.SPV.EventProof Chainweb.Test.Sync.WebBlockHeaderStore + Chainweb.Test.TestVersions Chainweb.Test.TreeDB Chainweb.Test.TreeDB.RemoteDB Chainweb.Test.Utils Chainweb.Test.Version Chainweb.Test.Utils.BlockHeader Chainweb.Test.Utils.TestHeader - Chainweb.Test.Utils.ApiQueries + -- Chainweb.Test.Utils.ApiQueries -- Data Data.Test.PQueue @@ -556,6 +566,7 @@ test-suite chainweb-tests , text >=1.2 , time >= 1.8 , transformers >= 0.5 + , MemoTrie , unordered-containers == 0.2.15.0 , vector >= 0.12.2 , wai >= 3.2 @@ -698,6 +709,7 @@ executable cwtool , process >= 1.5 , quickcheck-instances >= 0.3 , random >= 1.2 + , retry >= 0.9 , rocksdb-haskell-kadena >= 1.1.0 , safe-exceptions >= 0.1 , servant-client >= 0.18.2 @@ -710,6 +722,7 @@ executable cwtool , tasty-quickcheck >= 0.9 , temporary >= 1.3 , text >= 1.2 + , MemoTrie , unordered-containers == 0.2.15.0 , vector >= 0.12.2 , wai >= 3.2 diff --git a/docs/RecoveringFromDeepForks.md b/docs/RecoveringFromDeepForks.md index 29dde5dda7..a7245377ce 100644 --- a/docs/RecoveringFromDeepForks.md +++ b/docs/RecoveringFromDeepForks.md @@ -5,7 +5,7 @@ Help! My node has failed with the following fatal error: ``` Fatal error: Requested rewind exceeds limit (4). Our previous cut block height: 1, fork ancestor's block height: 6. Offending new block: -BlockHeader {_blockNonce = Nonce 0, _blockCreationTime = BlockCreationTime {_bct = Time (TimeSpan (Micros 1575672858613932))}, _blockParent = "ugAOff6YkKmvp_pJIHqx6HLTRPwyHEHD2c2Olm6L2Fg", _blockAdjacentHashes = BlockHashRecord {_getBlockHashRecord = fromList []}, _blockTarget = HashTarget (PowHashNat 115792089237316195423570985008687907853269984665640564039457584007913129639935), _blockPayloadHash = BlockPayloadHash (MerkleLogHash U-3paUKNKQUkggFmNOnGBEAJnjpy4K_-7t_dGyV0lhw), _blockChainId = ChainId 8, _blockWeight = BlockWeight (HashDifficulty (PowHashNat 1)), _blockHeight = 1, _blockChainwebVersion = fastTimedCPM-peterson, _blockEpochStart = EpochStartTime (Time (TimeSpan (Micros 0))), _blockFlags = FeatureFlags 0, _blockHash = "SKZ0mTWp08ooXsWz_yyT7v_MSrRdyKkmCyWJBTK8u3g"} +BlockHeader {_blockNonce = Nonce 0, _blockCreationTime = BlockCreationTime {_bct = Time (TimeSpan (Micros 1575672858613932))}, _blockParent = "ugAOff6YkKmvp_pJIHqx6HLTRPwyHEHD2c2Olm6L2Fg", _blockAdjacentHashes = BlockHashRecord {_getBlockHashRecord = fromList []}, _blockTarget = HashTarget (PowHashNat 115792089237316195423570985008687907853269984665640564039457584007913129639935), _blockPayloadHash = BlockPayloadHash (MerkleLogHash U-3paUKNKQUkggFmNOnGBEAJnjpy4K_-7t_dGyV0lhw), _blockChainId = ChainId 8, _blockWeight = BlockWeight (HashDifficulty (PowHashNat 1)), _blockHeight = 1, _blockChainwebVersion = fastfork-CPM-peterson, _blockEpochStart = EpochStartTime (Time (TimeSpan (Micros 0))), _blockFlags = FeatureFlags 0, _blockHash = "SKZ0mTWp08ooXsWz_yyT7v_MSrRdyKkmCyWJBTK8u3g"} Your node is part of a losing fork longer than your reorg-limit, which is a situation that requires manual intervention. diff --git a/node/ChainwebNode.hs b/node/ChainwebNode.hs index d58ec60618..f8a1768d2b 100644 --- a/node/ChainwebNode.hs +++ b/node/ChainwebNode.hs @@ -98,6 +98,7 @@ import Chainweb.Time import Chainweb.Utils import Chainweb.Utils.RequestLog import Chainweb.Version +import Chainweb.Version.Mainnet import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB @@ -128,18 +129,16 @@ data ChainwebNodeConfiguration = ChainwebNodeConfiguration makeLenses ''ChainwebNodeConfiguration -defaultChainwebNodeConfiguration :: ChainwebVersion -> ChainwebNodeConfiguration -defaultChainwebNodeConfiguration v = ChainwebNodeConfiguration - { _nodeConfigChainweb = defaultChainwebConfiguration v +defaultChainwebNodeConfiguration :: ChainwebNodeConfiguration +defaultChainwebNodeConfiguration = ChainwebNodeConfiguration + { _nodeConfigChainweb = defaultChainwebConfiguration Mainnet01 , _nodeConfigLog = defaultLogConfig & logConfigLogger . L.loggerConfigThreshold .~ level , _nodeConfigDatabaseDirectory = Nothing , _nodeConfigResetChainDbs = False } where - level = case v of - Mainnet01 -> L.Info - _ -> L.Info + level = L.Info validateChainwebNodeConfiguration :: ConfigValidation ChainwebNodeConfiguration [] validateChainwebNodeConfiguration o = do @@ -185,7 +184,7 @@ getBackupsDir conf = ( "backups") <$> getDbBaseDir conf getDbBaseDir :: HasCallStack => ChainwebNodeConfiguration -> IO FilePath getDbBaseDir conf = case _nodeConfigDatabaseDirectory conf of Nothing -> getXdgDirectory XdgData - $ "chainweb-node" sshow v + $ "chainweb-node" sshow (_versionName v) Just d -> return d where v = _configChainwebVersion $ _nodeConfigChainweb conf @@ -431,7 +430,7 @@ withNodeLogger logConfig v f = runManaged $ do liftIO $ f $ maybe id (\x -> addLabel ("cluster", toText x)) (_logConfigClusterId logConfig) - $ addLabel ("chainwebVersion", sshow v) + $ addLabel ("chainwebVersion", sshow (_versionName v)) $ logger where teleLogConfig = _logConfigTelemetryBackend logConfig @@ -517,7 +516,7 @@ mainInfo :: ProgramInfo ChainwebNodeConfiguration mainInfo = programInfoValidate "Chainweb Node" pChainwebNodeConfiguration - (defaultChainwebNodeConfiguration Mainnet01) + defaultChainwebNodeConfiguration validateChainwebNodeConfiguration handles :: [Handler a] -> IO a -> IO a diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 72aa6a391f..a71b07407f 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -102,6 +102,14 @@ module Chainweb.BlockHeader -- * Genesis BlockHeader , isGenesisBlockHeader +, genesisParentBlockHash +, genesisBlockHeader +, genesisBlockHeaders +, genesisBlockHeadersAtHeight +, genesisHeight +, headerSizes +, headerSizeBytes +, workSizeBytes -- * Create a new BlockHeader , newBlockHeader @@ -111,6 +119,7 @@ module Chainweb.BlockHeader ) where import Control.DeepSeq +import Control.Exception import Control.Lens hiding ((.=)) import Control.Monad.Catch @@ -119,13 +128,19 @@ import Data.Aeson.Types (Parser) import Data.Function (on) import Data.Hashable import qualified Data.HashMap.Strict as HM +import Data.HashMap.Strict (HashMap) import qualified Data.HashSet as HS +import Data.IORef +import qualified Data.List.NonEmpty as NE import Data.Kind import qualified Data.Memory.Endian as BA +import Data.MerkleLog hiding (Actual, Expected, MerkleHash) import qualified Data.Text as T import Data.Word import GHC.Generics (Generic) +import GHC.Stack +import Numeric.Natural -- Internal imports @@ -144,13 +159,20 @@ import Chainweb.PowHash import Chainweb.Time import Chainweb.TreeDB (TreeDbEntry(..)) import Chainweb.Utils +import Chainweb.Utils.Rule import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Guards +import Chainweb.Version.Mainnet +import Chainweb.Version.Registry import Chainweb.Storage.Table +import Crypto.Hash.Algorithms + import Numeric.AffineSpace +import System.IO.Unsafe import Text.Read (readEither) -- -------------------------------------------------------------------------- -- @@ -168,8 +190,6 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag Nonce where type Tag Nonce = 'BlockNonceTag toMerkleNode = encodeMerkleInputNode encodeNonce fromMerkleNode = decodeMerkleInputNode decodeNonce - {-# INLINE toMerkleNode #-} - {-# INLINE fromMerkleNode #-} encodeNonce :: Nonce -> Put encodeNonce (Nonce n) = putWord64le n @@ -183,8 +203,6 @@ decodeNonce = Nonce <$> getWord64le instance ToJSON Nonce where toJSON (Nonce i) = toJSON $ show i toEncoding (Nonce i) = toEncoding $ show i - {-# INLINE toJSON #-} - {-# INLINE toEncoding #-} instance FromJSON Nonce where parseJSON = withText "Nonce" @@ -202,8 +220,6 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag EpochStartT type Tag EpochStartTime = 'EpochStartTimeTag toMerkleNode = encodeMerkleInputNode encodeEpochStartTime fromMerkleNode = decodeMerkleInputNode decodeEpochStartTime - {-# INLINE toMerkleNode #-} - {-# INLINE fromMerkleNode #-} encodeEpochStartTime :: EpochStartTime -> Put encodeEpochStartTime (EpochStartTime t) = encodeTime t @@ -216,14 +232,11 @@ decodeEpochStartTime = EpochStartTime <$> decodeTime -- early stages of the network. -- effectiveWindow :: BlockHeader -> Maybe WindowWidth -effectiveWindow h = WindowWidth <$> case window ver of +effectiveWindow h = WindowWidth <$> case _versionWindow (_chainwebVersion h) of Nothing -> Nothing Just (WindowWidth w) | int (_blockHeight h) <= w -> Just $ max 1 $ w `div` 10 | otherwise -> Just w - where - ver = _blockChainwebVersion h -{-# INLINE effectiveWindow #-} -- | Return whether the given 'BlockHeader' is the last header in its epoch. -- @@ -233,7 +246,6 @@ isLastInEpoch h = case effectiveWindow h of Just (WindowWidth w) | (int (_blockHeight h) + 1) `mod` w == 0 -> True | otherwise -> False -{-# INLINE isLastInEpoch #-} -- | If it is discovered that the last DA occured significantly in the past, we -- assume that a large amount of hash power has suddenly dropped out of the @@ -251,11 +263,11 @@ slowEpoch (ParentHeader p) (BlockCreationTime ct) = actual > (expected * 5) BlockRate s = blockRate (_blockChainwebVersion p) WindowWidth ww = fromJuste $ window (_blockChainwebVersion p) - expected :: Seconds + expected :: Micros expected = s * int ww - actual :: Seconds - actual = timeSpanToSeconds $ ct .-. es + actual :: Micros + actual = timeSpanToMicros $ ct .-. es -- | Compute the POW target for a new BlockHeader. -- @@ -282,24 +294,24 @@ powTarget p@(ParentHeader ph) as bct = case effectiveWindow ph of Nothing -> maxTarget Just w -- Emergency DA, legacy - | slowEpochGuard ver (_blockHeight ph) && slowEpoch p bct -> + | slowEpochGuard ver (_chainId ph) (_blockHeight ph) && slowEpoch p bct -> activeAdjust w | isLastInEpoch ph -> activeAdjust w | otherwise -> _blockTarget ph where - t = EpochStartTime $ if oldTargetGuard ver (_blockHeight ph) + ver = _chainwebVersion ph + t = EpochStartTime $ if oldTargetGuard ver (_chainId ph) (_blockHeight ph) then _bct bct else _bct (_blockCreationTime ph) - ver = _chainwebVersion p activeAdjust w - | oldDaGuard ver (_blockHeight ph + 1) - = legacyAdjust ver w (t .-. _blockEpochStart ph) (_blockTarget ph) + | oldDaGuard ver (_chainId ph) (_blockHeight ph + 1) + = legacyAdjust (_versionBlockRate ver) w (t .-. _blockEpochStart ph) (_blockTarget ph) | otherwise = avgTarget $ adjustForParent w <$> (p : HM.elems as) adjustForParent w (ParentHeader a) - = adjust ver w (toEpochStart a .-. _blockEpochStart a) (_blockTarget a) + = adjust (_versionBlockRate ver) w (toEpochStart a .-. _blockEpochStart a) (_blockTarget a) toEpochStart = EpochStartTime . _bct . _blockCreationTime @@ -307,8 +319,6 @@ powTarget p@(ParentHeader ph) as bct = case effectiveWindow ph of where s = sum $ fmap (int @_ @Rational . _hashTarget) targets -{-# INLINE powTarget #-} - -- | Compute the epoch start value for a new BlockHeader -- epochStart @@ -330,7 +340,7 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) -- A special case for starting a new devnet, to compensate the inaccurate -- creation time of the genesis blocks. This would result in a very long -- first epoch that cause a trivial target in the second epoch. - | ver == Development && _blockHeight p == 1 = EpochStartTime (_bct $ _blockCreationTime p) + | _versionFakeFirstEpochStart ver, _blockHeight p == 1 = EpochStartTime (_bct $ _blockCreationTime p) -- New Graph: the block time of the genesis block isn't accurate, we thus -- use the block time of the first block on the chain. Depending on where @@ -340,13 +350,13 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) | parentIsFirstOnNewChain = EpochStartTime (_bct $ _blockCreationTime p) -- End of epoch, DA adjustment (legacy version) - | isLastInEpoch p && oldTargetGuard ver (_blockHeight p) = EpochStartTime bt + | isLastInEpoch p && oldTargetGuard ver (_chainId p) (_blockHeight p) = EpochStartTime bt -- End of epoch, DA adjustment | isLastInEpoch p = EpochStartTime (_bct $ _blockCreationTime p) -- Within epoch with old legacy DA - | oldDaGuard ver (_blockHeight p + 1) = _blockEpochStart p + | oldDaGuard ver (_chainId p) (_blockHeight p + 1) = _blockEpochStart p -- Within an epoch with new DA | otherwise = _blockEpochStart p @@ -413,7 +423,6 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) parentIsFirstOnNewChain = _blockHeight p > 1 && _blockHeight p == genesisHeight ver cid + 1 -{-# INLINE epochStart #-} -- ----------------------------------------------------------------------------- -- Feature Flags @@ -433,8 +442,6 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag FeatureFlag type Tag FeatureFlags = 'FeatureFlagsTag toMerkleNode = encodeMerkleInputNode encodeFeatureFlags fromMerkleNode = decodeMerkleInputNode decodeFeatureFlags - {-# INLINE toMerkleNode #-} - {-# INLINE fromMerkleNode #-} mkFeatureFlags :: FeatureFlags mkFeatureFlags = FeatureFlags 0x0 @@ -458,15 +465,148 @@ parentHeader = lens _parentHeader $ \_ hdr -> ParentHeader hdr instance HasChainId ParentHeader where _chainId = _chainId . _parentHeader - {-# INLINE _chainId #-} instance HasChainwebVersion ParentHeader where _chainwebVersion = _chainwebVersion . _parentHeader - {-# INLINE _chainwebVersion #-} instance HasChainGraph ParentHeader where _chainGraph = _chainGraph . _parentHeader - {-# INLINE _chainGraph #-} + +isGenesisBlockHeader :: BlockHeader -> Bool +isGenesisBlockHeader b = + _blockHeight b == genesisHeight (_chainwebVersion b) (_chainId b) + +-- +-- | The genesis block hash includes the Chainweb version and the 'ChainId' +-- within the Chainweb version. +-- +-- It is the '_blockParent' of the genesis block +-- +genesisParentBlockHash :: HasChainId p => ChainwebVersion -> p -> BlockHash +genesisParentBlockHash v p = BlockHash $ MerkleLogHash + $ merkleRoot $ merkleTree @ChainwebMerkleHashAlgorithm + [ InputNode "CHAINWEB_GENESIS" + , encodeMerkleInputNode encodeChainwebVersionCode (_versionCode v) + , encodeMerkleInputNode encodeChainId (_chainId p) + ] + +{-# noinline genesisBlockHeaderCache #-} +genesisBlockHeaderCache :: IORef (HashMap ChainwebVersionCode (HashMap ChainId BlockHeader)) +genesisBlockHeaderCache = unsafePerformIO $ do + let mkMainnetHeader = makeGenesisBlockHeader mainnet + newIORef $ HM.singleton (_versionCode mainnet) $ HM.fromList + [ (cid, mkMainnetHeader cid) + | cid <- HS.toList (chainIds mainnet) + ] + +-- | A block chain is globally uniquely identified by its genesis hash. +-- Internally, we use the 'ChainwebVersionTag value and the 'ChainId' +-- as identifiers. We thus include the 'ChainwebVersionTag value and the +-- 'ChainId' into the genesis block hash. +-- +-- We assume that there is always only a single 'ChainwebVersionTag in +-- scope and identify chains only by their internal 'ChainId'. +-- +genesisBlockHeaders :: ChainwebVersion -> HashMap ChainId BlockHeader +genesisBlockHeaders v = unsafePerformIO $ + HM.lookup (_versionCode v) <$> readIORef genesisBlockHeaderCache >>= \case + Just hs -> return hs + Nothing -> do + modifyIORef' genesisBlockHeaderCache $ HM.insert (_versionCode v) freshGenesisHeaders + return freshGenesisHeaders + where + freshGenesisHeaders = + HM.fromList [ (cid, makeGenesisBlockHeader v cid) | cid <- HS.toList (chainIds v) ] + +genesisBlockHeader :: (HasCallStack, HasChainId p) => ChainwebVersion -> p -> BlockHeader +genesisBlockHeader v p = genesisBlockHeaders v ^?! at (_chainId p) . _Just + +makeGenesisBlockHeader :: ChainwebVersion -> ChainId -> BlockHeader +makeGenesisBlockHeader v cid = + makeGenesisBlockHeader' v cid (_genesisTime (_versionGenesis v) ^?! onChain cid) (Nonce 0) + +genesisHeight' :: HasCallStack => ChainwebVersion -> ChainId -> BlockHeight +genesisHeight' v c = fst + $ head + $ NE.dropWhile (not . flip isWebChain c . snd) + $ NE.reverse (ruleElems (BlockHeight 0) $ _versionGraphs v) + +-- | Like `genesisBlockHeader`, but with slightly more control. +-- +-- This call generates the block header from the definitions in +-- "Chainweb.Version". It is a somewhat expensive call, since it involves +-- building the Merkle tree. +-- +makeGenesisBlockHeader' + :: HasChainId p + => ChainwebVersion + -> p + -> BlockCreationTime + -> Nonce + -> BlockHeader +makeGenesisBlockHeader' v p ct@(BlockCreationTime t) n = + fromLog @ChainwebMerkleHashAlgorithm mlog + where + g = genesisGraph v p + cid = _chainId p + + mlog = newMerkleLog + $ mkFeatureFlags + :+: ct + :+: genesisParentBlockHash v cid + :+: (v ^?! versionGenesis . genesisBlockTarget . onChain cid) + :+: genesisBlockPayloadHash v cid + :+: cid + :+: BlockWeight 0 + :+: genesisHeight' v cid -- because of chain graph changes (new chains) not all chains start at 0 + :+: _versionCode v + :+: EpochStartTime t + :+: n + :+: MerkleLogBody (blockHashRecordToVector adjParents) + adjParents = BlockHashRecord $ HM.fromList $ + (\c -> (c, genesisParentBlockHash v c)) <$> HS.toList (adjacentChainIds g p) + +-- | The set of genesis block headers as it exited at a particular block height +-- +genesisBlockHeadersAtHeight + :: ChainwebVersion + -> BlockHeight + -> HashMap ChainId BlockHeader +genesisBlockHeadersAtHeight v h = + HM.filter (\hdr -> _blockHeight hdr <= h) (genesisBlockHeaders v) +-- +-- -------------------------------------------------------------------------- -- +-- Genesis Height +-- +-- | The genesis graph for a given Chain +-- +-- Invariant: +-- +-- * The given ChainId exists in the first graph of the graph history. +-- (We generally assume that this invariant holds throughout the code base. +-- It is enforced via the 'mkChainId' smart constructor for ChainId.) +-- +genesisGraph + :: HasCallStack + => HasChainwebVersion v + => HasChainId c + => v + -> c + -> ChainGraph +genesisGraph v = chainGraphAt v_ . genesisHeight' v_ . _chainId + where + v_ = _chainwebVersion v + +-- | Returns the height of the genesis block for a chain. +-- +-- Invariant: +-- +-- * The given ChainId exists in the first graph of the graph history. +-- (We generally assume that this invariant holds throughout the code base. +-- It is enforced via the 'mkChainId' smart constructor for ChainId.) +-- +genesisHeight :: HasCallStack => ChainwebVersion -> ChainId -> BlockHeight +genesisHeight v c = _blockHeight (genesisBlockHeader v c) -- -------------------------------------------------------------------------- -- -- Block Header @@ -529,7 +669,8 @@ data BlockHeader :: Type where , _blockParent :: {-# UNPACK #-} !BlockHash -- ^ authoritative - , _blockAdjacentHashes :: !BlockHashRecord + , _blockAdjacentHashes :: BlockHashRecord + -- edtodo: document why this is lazy -- ^ authoritative , _blockTarget :: {-# UNPACK #-} !HashTarget @@ -575,39 +716,27 @@ data BlockHeader :: Type where deriving (Show, Generic) deriving anyclass (NFData) -isGenesisBlockHeader :: BlockHeader -> Bool -isGenesisBlockHeader b = - _blockHeight b == genesisHeight (_blockChainwebVersion b) (_blockChainId b) -{-# INLINE isGenesisBlockHeader #-} - instance Eq BlockHeader where (==) = (==) `on` _blockHash - {-# INLINE (==) #-} instance Ord BlockHeader where compare = compare `on` _blockHash - {-# INLINE compare #-} instance Hashable BlockHeader where hashWithSalt s = hashWithSalt s . _blockHash - {-# INLINE hashWithSalt #-} instance HasChainId BlockHeader where _chainId = _blockChainId - {-# INLINE _chainId #-} instance HasChainGraph BlockHeader where _chainGraph h = _chainGraph (_blockChainwebVersion h, _blockHeight h) - {-# INLINE _chainGraph #-} instance HasChainwebVersion BlockHeader where _chainwebVersion = _blockChainwebVersion - {-# INLINE _chainwebVersion #-} instance IsCasValue BlockHeader where type CasKeyType BlockHeader = BlockHash casKey = _blockHash - {-# INLINE casKey #-} type BlockHeaderCas tbl = Cas tbl BlockHeader @@ -625,7 +754,7 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh , ChainId , BlockWeight , BlockHeight - , ChainwebVersion + , ChainwebVersionCode , EpochStartTime , Nonce ] @@ -643,7 +772,7 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh :+: _blockChainId bh :+: _blockWeight bh :+: _blockHeight bh - :+: _blockChainwebVersion bh + :+: _versionCode (_blockChainwebVersion bh) :+: _blockEpochStart bh :+: _blockNonce bh :+: MerkleLogBody (blockHashRecordToVector $ _blockAdjacentHashes bh) @@ -672,14 +801,15 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh :+: cid :+: weight :+: height - :+: cwv + :+: cwvc :+: es :+: nonce :+: MerkleLogBody adjParents ) = _merkleLogEntries l + cwv = lookupVersionByCode cwvc adjGraph - | height == genesisHeight cwv cid = chainGraphAt cwv height + | height == genesisHeight' cwv cid = chainGraphAt cwv height | otherwise = chainGraphAt cwv (height - 1) encodeBlockHeaderWithoutHash :: BlockHeader -> Put @@ -693,7 +823,7 @@ encodeBlockHeaderWithoutHash b = do encodeChainId (_blockChainId b) encodeBlockWeight (_blockWeight b) encodeBlockHeight (_blockHeight b) - encodeChainwebVersion (_blockChainwebVersion b) + encodeChainwebVersionCode (_versionCode $ _blockChainwebVersion b) encodeEpochStartTime (_blockEpochStart b) encodeNonce (_blockNonce b) @@ -741,7 +871,7 @@ decodeBlockHeaderWithoutHash = do a6 <- decodeChainId a7 <- decodeBlockWeight a8 <- decodeBlockHeight - a9 <- decodeChainwebVersion + a9 <- decodeChainwebVersionCode a11 <- decodeEpochStartTime a12 <- decodeNonce return @@ -773,16 +903,14 @@ decodeBlockHeader = BlockHeader <*> decodeChainId <*> decodeBlockWeight <*> decodeBlockHeight - <*> decodeChainwebVersion + <*> (lookupVersionByCode <$> decodeChainwebVersionCode) <*> decodeEpochStartTime <*> decodeNonce <*> decodeBlockHash instance ToJSON BlockHeader where - toJSON = toJSON . encodeB64UrlNoPaddingText . runPutS . encodeBlockHeader - toEncoding = toEncoding . encodeB64UrlNoPaddingText . runPutS . encodeBlockHeader - {-# INLINE toJSON #-} - {-# INLINE toEncoding #-} + toJSON = toJSON . encodeB64UrlNoPaddingText . runPutS . encodeBlockHeader + toEncoding = toEncoding . encodeB64UrlNoPaddingText . runPutS . encodeBlockHeader instance FromJSON BlockHeader where parseJSON = withText "BlockHeader" $ \t -> @@ -806,23 +934,20 @@ getAdjacentHash p b = firstOf (blockAdjacentHashes . ixg (_chainId p)) b ??? ChainNotAdjacentException (Expected $ _chainId p) (Actual $ _blockAdjacentChainIds b) -{-# INLINE getAdjacentHash #-} computeBlockHash :: BlockHeader -> BlockHash computeBlockHash h = BlockHash $ MerkleLogHash $ computeMerkleLogRoot h -{-# INLINE computeBlockHash #-} -- | The Proof-Of-Work hash includes all data in the block except for the -- '_blockHash'. The value (interpreted as 'BlockHashNat' must be smaller than -- the value of '_blockTarget' (interpreted as 'BlockHashNat'). -- _blockPow :: BlockHeader -> PowHash -_blockPow h = powHash (_blockChainwebVersion h) +_blockPow h = cryptoHash @Blake2s_256 $ runPutS $ encodeBlockHeaderWithoutHash h blockPow :: Getter BlockHeader PowHash blockPow = to _blockPow -{-# INLINE blockPow #-} -- | The number of microseconds between the creation time of two `BlockHeader`s. -- @@ -864,18 +989,15 @@ blockHeaderProperties (ObjectEncoded b) = , "chainId" .= _chainId b , "weight" .= _blockWeight b , "height" .= _blockHeight b - , "chainwebVersion" .= _blockChainwebVersion b + , "chainwebVersion" .= _versionCode (_blockChainwebVersion b) , "epochStart" .= _blockEpochStart b , "featureFlags" .= _blockFlags b , "hash" .= _blockHash b ] -{-# INLINE blockHeaderProperties #-} instance ToJSON (ObjectEncoded BlockHeader) where toJSON = object . blockHeaderProperties toEncoding = pairs . mconcat . blockHeaderProperties - {-# INLINE toJSON #-} - {-# INLINE toEncoding #-} parseBlockHeaderObject :: Object -> Parser BlockHeader parseBlockHeaderObject o = BlockHeader @@ -888,7 +1010,7 @@ parseBlockHeaderObject o = BlockHeader <*> o .: "chainId" <*> o .: "weight" <*> o .: "height" - <*> o .: "chainwebVersion" + <*> (lookupVersionByCode <$> o .: "chainwebVersion") <*> o .: "epochStart" <*> o .: "nonce" <*> o .: "hash" @@ -896,7 +1018,6 @@ parseBlockHeaderObject o = BlockHeader instance FromJSON (ObjectEncoded BlockHeader) where parseJSON = withObject "BlockHeader" $ fmap ObjectEncoded . parseBlockHeaderObject - {-# INLINE parseJSON #-} -- -------------------------------------------------------------------------- -- -- IsBlockHeader @@ -954,7 +1075,7 @@ newBlockHeader adj pay nonce t p@(ParentHeader b) = :+: cid :+: _blockWeight b + BlockWeight (targetToDifficulty target) :+: _blockHeight b + 1 - :+: v + :+: _versionCode v :+: epochStart p adj t :+: nonce :+: MerkleLogBody (blockHashRecordToVector adjHashes) @@ -974,3 +1095,57 @@ instance TreeDbEntry BlockHeader where parent e | isGenesisBlockHeader e = Nothing | otherwise = Just (_blockParent e) + +-- | This is an internal function. Use 'headerSizeBytes' instead. +-- +-- Postconditions: for all @v@ +-- +-- * @not . null $ headerSizes v@, and +-- * @0 == (fst . last) (headerSizes v)@. +-- +-- Note that for all but genesis headers the number of adjacent hashes depends +-- on the graph of the parent. +-- +headerSizes :: ChainwebVersion -> Rule BlockHeight Natural +headerSizes v = fmap (\g -> _versionHeaderBaseSizeBytes v + 36 * degree g + 2) $ _versionGraphs v + +-- | The size of the serialized block header. +-- +-- This function is safe because of the invariant of 'headerSize' that there +-- exists and entry for block height 0. +-- +-- Note that for all but genesis headers the number of adjacent hashes depends +-- on the graph of the parent. +-- +headerSizeBytes + :: HasCallStack + => ChainwebVersion + -> ChainId + -> BlockHeight + -> Natural +headerSizeBytes v cid h = snd + $ ruleHead + $ ruleDropWhile (> relevantHeight) + $ headerSizes v + where + relevantHeight + | genesisHeight v cid == h = h + | otherwise = h - 1 + +-- | The size of the work bytes /without/ the preamble of the chain id and target +-- +-- The chain graph, and therefore also the header size, is constant for all +-- blocks at the same height except for genesis blocks. Because genesis blocks +-- are never mined, we can ignore this difference here and just return the +-- result for chain 0. +-- +-- NOTE: For production versions we require that the value is constant for a +-- given chainweb version. This would only ever change as part of the +-- introduction of new block header format. +-- +workSizeBytes + :: HasCallStack + => ChainwebVersion + -> BlockHeight + -> Natural +workSizeBytes v h = headerSizeBytes v (unsafeChainId 0) h - 32 diff --git a/src/Chainweb/BlockHeader/Genesis.hs b/src/Chainweb/BlockHeader/Genesis.hs deleted file mode 100644 index d9070a911d..0000000000 --- a/src/Chainweb/BlockHeader/Genesis.hs +++ /dev/null @@ -1,356 +0,0 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-# LANGUAGE TypeApplications #-} - --- | --- Module: Chainweb.BlockHeader.Genesis --- Copyright: Copyright © 2018 - 2020 Kadena LLC. --- License: MIT --- Maintainer: Colin Woodbury --- Stability: experimental --- --- Hard-coded Genesis blocks for various versions of Chainweb. --- -module Chainweb.BlockHeader.Genesis - ( -- * Genesis Blocks - -- ** Creation - genesisBlockHeader - , genesisBlockHeader' - , genesisBlockHeaders - , genesisBlockHeadersAtHeight - -- ** Querying - , genesisBlockPayload - , genesisParentBlockHash - , genesisBlockTarget - , genesisTime - -- * No-op payloads - , emptyPayload - - -- * Genesis targets - , mainnet20InitialHashTarget - , testnet20InitialHashTarget - ) where - -import Control.Arrow ((&&&)) - -import Data.Foldable (toList) -import qualified Data.HashMap.Strict as HM -import qualified Data.HashSet as HS -import Data.MerkleLog hiding (Actual, Expected, MerkleHash) - --- internal modules - -import Chainweb.BlockCreationTime -import Chainweb.BlockHash -import Chainweb.BlockHeader -import qualified Chainweb.BlockHeader.Genesis.Development0Payload as DN0 -import qualified Chainweb.BlockHeader.Genesis.DevelopmentNPayload as DNN -import qualified Chainweb.BlockHeader.Genesis.DevelopmentKADPayload as DNKAD -import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload as TN0 -import qualified Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload as TNN -import qualified Chainweb.BlockHeader.Genesis.Mainnet0Payload as MN0 -import qualified Chainweb.BlockHeader.Genesis.Mainnet1Payload as MN1 -import qualified Chainweb.BlockHeader.Genesis.Mainnet2Payload as MN2 -import qualified Chainweb.BlockHeader.Genesis.Mainnet3Payload as MN3 -import qualified Chainweb.BlockHeader.Genesis.Mainnet4Payload as MN4 -import qualified Chainweb.BlockHeader.Genesis.Mainnet5Payload as MN5 -import qualified Chainweb.BlockHeader.Genesis.Mainnet6Payload as MN6 -import qualified Chainweb.BlockHeader.Genesis.Mainnet7Payload as MN7 -import qualified Chainweb.BlockHeader.Genesis.Mainnet8Payload as MN8 -import qualified Chainweb.BlockHeader.Genesis.Mainnet9Payload as MN9 -import qualified Chainweb.BlockHeader.Genesis.MainnetKADPayload as MNKAD -import qualified Chainweb.BlockHeader.Genesis.Testnet0Payload as PN0 -import qualified Chainweb.BlockHeader.Genesis.TestnetNPayload as PNN -import Chainweb.BlockHeight -import Chainweb.BlockWeight -import Chainweb.Crypto.MerkleLog -import Chainweb.Difficulty (HashTarget(..), maxTarget) -import Chainweb.MerkleLogHash -import Chainweb.MerkleUniverse -import Chainweb.Miner.Pact -import Chainweb.Payload -import Chainweb.Time -import Chainweb.Utils -import Chainweb.Version - - --- -------------------------------------------------------------------------- -- --- Genesis BlockHeader - --- | The genesis block hash includes the Chainweb version and the 'ChainId' --- within the Chainweb version. --- --- It is the '_blockParent' of the genesis block --- -genesisParentBlockHash :: HasChainId p => ChainwebVersion -> p -> BlockHash -genesisParentBlockHash v p = BlockHash $ MerkleLogHash - $ merkleRoot $ merkleTree @ChainwebMerkleHashAlgorithm - [ InputNode "CHAINWEB_GENESIS" - , encodeMerkleInputNode encodeChainwebVersion v - , encodeMerkleInputNode encodeChainId (_chainId p) - ] - --- | By definition, Genesis Blocks are "mined" on the easiest difficulty. No --- subsequent block mining can have a `HashTarget` easier (re: higher) than --- this. Equivalent to `maxTarget`. --- --- When the graph is extended new chains should "enter" with a non-trivial --- difficulty in order to avoid races and resulting forks during the first two --- or three difficulty adjustement epochs. --- --- On devnet, using maxTarget results in a too high block production and --- consecutively orphans and network congestion. The consequence are --- osciallations to take serval hundred blocks before the system stabilizes. --- This setting cools down initial block production. --- --- TODO: move this and the following definitions to Chainweb.Version (or a --- submodule of Chainweb.Version`). --- -genesisBlockTarget :: ChainwebVersion -> ChainId -> HashTarget -genesisBlockTarget v@Mainnet01 cid - | genesisHeight v cid > 731382 = mainnet20InitialHashTarget -genesisBlockTarget v@Testnet04 cid - | genesisHeight v cid > 278626 = testnet20InitialHashTarget -genesisBlockTarget v@Development cid - | genesisHeight v cid > (to20ChainsDevelopment - (min to20ChainsDevelopment 10)) = - HashTarget 0x0000088f99632cadf39b0db7655be62cb7dbc84ebbd9a90e5b5756d3e7d9196c - -- 4 * 10 node-mining - | otherwise = HashTarget (maxBound `div` 100000) -genesisBlockTarget _ _ = maxTarget - --- | Initial hash target for mainnet 20-chain transition. Difficulty on the new --- chains is 1/4 of the current difficulty. It is based on the following header --- from 2020-07-09. This value should be double checked after the testnet --- transition and before the release of chainweb node version 2.1. --- --- @ --- { --- "creationTime": 1594319266887602, --- "parent": "aSIkDjuJQGGOwJW-60T_1WRK9KPJm1rz63a4SW8WtSc", --- "height": 731382, --- "hash": "Ua_pSMMo-szlMpXMuSYWTcVlaSIf01TxJvBCmFkmhBM", --- "chainId": 0, --- "weight": "xo3dabqEYpUPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", --- "featureFlags": 0, --- "epochStart": 1594316109999615, --- "adjacents": { --- "2": "KuuujcD6yeZ9jRXwlRE0ed5dHc3x_akIz1REmKXuDtk", --- "5": "qFU32Qmlj-syzuZ2awCvyoW6Jex3TQqGTzd-Dchn1gc", --- "3": "Lgu1FgiCw4qPpptoVRmijn8WKG2OcAUAp1Ha7KFbrWg" --- }, --- "payloadHash": "MV079yClHYSYBW74WySK-15AUVQg8QMKHJZbtzTCbgA", --- "chainwebVersion": "mainnet01", --- "target": "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA", --- "nonce": "149742924667593745" --- } --- @ --- --- It holds that: --- --- prop> Just mainnet20InitialHashTarget == HashTarget . (4 *) <$> (runGet decodePowHashNat =<< decodeB64UrlNoPaddingText "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") --- -mainnet20InitialHashTarget :: HashTarget -mainnet20InitialHashTarget = HashTarget 0x000000000000cb73de4be95ba21db5b9178dd85974c194e3ee05717dd8afa830 - --- | Initial hash target for testnet 20-chain transition. Based on the following --- header from devnet running with 5 GPUs hash power. Using this target unchanged --- means, that we should do to the transition with the hash power of about --- 5 - 50 GPUs in the system for a smooth transition. --- --- The value for the initial target is 38 times smaller larger than value of an --- successful test run on devnet with 5 GPUs. During that test the initial --- target was about 32 times larger than the actual target at the time of the --- transition. --- --- @ --- { --- "creationTime": 1594433454304125, --- "parent": "DHSarVwhj6Xvu0KewCI1nRdGcNSWKFoOUy7us27mDac", --- "height": 200, --- "hash": "DC8HV9W0JM5gzliwDupjG10Lnwav09xWtxy01kGPTLM", --- "chainId": 0, --- "weight": "ReZ2aCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", --- "featureFlags": 0, --- "epochStart": 1594430808323849, --- "adjacents": { --- "2": "JPbz_YjWIvDgdGxdemkU6vVRimZZawxY_j0Hwo0pzb0", --- "5": "wMFfoFrQ1GWOFj6jCNGRa3SiuFRGOCmS06F7HfmLnNw", --- "3": "9WIBnxDGGZsy9FCCorvAUa4SlE5Rqs-cTLEsWCPOVbQ" --- }, --- "payloadHash": "AOYQdE5xl_YueZSppW4MoadasjF149K28CON2GuH9Mc", --- "chainwebVersion": "development", --- "target": "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA", --- "nonce": "5805155470630695" --- } --- @ --- --- It holds that: --- --- prop> Just testnet20InitialHashTarget == HashTarget <$> (runGet decodePowHashNat =<< decodeB64UrlNoPaddingText "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") --- prop> _hashTarget testnet20InitialHashTarget `div` _hashTarget mainnet20InitialHashTarget == PowHashNat 8893 --- prop> _hashTarget (genesisBlockTarget Development (unsafeChainId 10)) `div` _hashTarget testnet20InitialHashTarget == PowHashNat 38 --- -testnet20InitialHashTarget :: HashTarget -testnet20InitialHashTarget = HashTarget 0x000000001b9bf15be43824bae4c4f17722572883f7b53ed2e8c6ba9596249235 - --- | Empty payload marking no-op transaction payloads for deprecated --- versions. --- -emptyPayload :: PayloadWithOutputs -emptyPayload = PayloadWithOutputs mempty miner coinbase h i o - where - (BlockPayload h i o) = newBlockPayload miner coinbase mempty - miner = MinerData $ encodeToByteString noMiner - coinbase = noCoinbaseOutput - --- | The moment of creation of a Genesis Block. For test chains, this is the --- Linux Epoch. Production chains are otherwise fixed to a specific timestamp. --- -genesisTime :: ChainwebVersion -> ChainId -> BlockCreationTime -genesisTime Test{} _ = BlockCreationTime epoch -genesisTime TimedConsensus{} _ = BlockCreationTime epoch -genesisTime PowConsensus{} _ = BlockCreationTime epoch -genesisTime TimedCPM{} _ = BlockCreationTime epoch -genesisTime FastTimedCPM{} _ = BlockCreationTime epoch -genesisTime Development _ = BlockCreationTime [timeMicrosQQ| 2019-07-17T18:28:37.613832 |] -genesisTime Testnet04 _ = BlockCreationTime [timeMicrosQQ| 2019-07-17T18:28:37.613832 |] -genesisTime Mainnet01 _ = BlockCreationTime [timeMicrosQQ| 2019-10-30T00:01:00.0 |] - -genesisBlockPayloadHash :: ChainwebVersion -> ChainId -> BlockPayloadHash -genesisBlockPayloadHash v = _payloadWithOutputsPayloadHash . genesisBlockPayload v - --- TODO when Payload DB is finally loading genesis post-sync and post-pact, --- the genesis block payload should be PayloadData, and PayloadWithOutputs --- should have the TransactionTree and OutputTree to avoid recreating those --- in PayloadStore. -genesisBlockPayload :: ChainwebVersion -> ChainId -> PayloadWithOutputs --- Test Instances -genesisBlockPayload Test{} _ = emptyPayload -genesisBlockPayload TimedConsensus{} _ = emptyPayload -genesisBlockPayload PowConsensus{} _ = emptyPayload -genesisBlockPayload TimedCPM{} cid = case chainIdInt @Int cid of - 0 -> TN0.payloadBlock - _ -> TNN.payloadBlock - -genesisBlockPayload FastTimedCPM{} cid = case chainIdInt @Int cid of - 0 -> TN0.payloadBlock - _ -> TNN.payloadBlock - --- Development Instances -genesisBlockPayload Development cid = case chainIdInt @Int cid of - 0 -> DN0.payloadBlock - c | c >= 1, c <= 9 -> DNN.payloadBlock - c | c >= 10, c <= 19 -> DNKAD.payloadBlock - _ -> error "chainweb graph only supports a maximum of 20 chains - please review" - --- Production Instances -genesisBlockPayload Testnet04 cid = case chainIdInt @Int cid of - 0 -> PN0.payloadBlock - _ -> PNN.payloadBlock - -genesisBlockPayload Mainnet01 cid = case chainIdInt @Int cid of - 0 -> MN0.payloadBlock - 1 -> MN1.payloadBlock - 2 -> MN2.payloadBlock - 3 -> MN3.payloadBlock - 4 -> MN4.payloadBlock - 5 -> MN5.payloadBlock - 6 -> MN6.payloadBlock - 7 -> MN7.payloadBlock - 8 -> MN8.payloadBlock - 9 -> MN9.payloadBlock - c | c >= 10, c <= 19 -> MNKAD.payloadBlock - _ -> error "chainweb graph only supports a maximum of 20 chains - please review" - --- | A block chain is globally uniquely identified by its genesis hash. --- Internally, we use the 'ChainwebVersion' value and the 'ChainId' --- as identifiers. We thus include the 'ChainwebVersion' value and the --- 'ChainId' into the genesis block hash. --- --- We assume that there is always only a single 'ChainwebVersion' in --- scope and identify chains only by their internal 'ChainId'. --- -genesisBlockHeader :: HasChainId p => ChainwebVersion -> p -> BlockHeader -genesisBlockHeader Mainnet01 p = fromJuste $ HM.lookup (_chainId p) genesisBlockHeadersMainnet01 -genesisBlockHeader Testnet04 p = fromJuste $ HM.lookup (_chainId p) genesisBlockHeadersTestnet04 -genesisBlockHeader Development p = fromJuste $ HM.lookup (_chainId p) genesisBlockHeadersDevelopment -genesisBlockHeader v p = genesisBlockHeaderInternal v (_chainId p) - -genesisBlockHeaderInternal :: ChainwebVersion -> ChainId -> BlockHeader -genesisBlockHeaderInternal v cid = genesisBlockHeader' v cid (genesisTime v cid) (Nonce 0) - --- | Like `genesisBlockHeader`, but with slightly more control. --- --- This call generates the block header from the definitions in --- "Chainweb.Version". It is a somewhat expensive call, since it involves --- building the Merkle tree. --- -genesisBlockHeader' - :: HasChainId p - => ChainwebVersion - -> p - -> BlockCreationTime - -> Nonce - -> BlockHeader -genesisBlockHeader' v p ct@(BlockCreationTime t) n = - fromLog @ChainwebMerkleHashAlgorithm mlog - where - g = genesisGraph v p - cid = _chainId p - - mlog = newMerkleLog - $ mkFeatureFlags - :+: ct - :+: genesisParentBlockHash v cid - :+: genesisBlockTarget v cid - :+: genesisBlockPayloadHash v cid - :+: cid - :+: BlockWeight 0 - :+: genesisHeight v cid -- because of chain graph changes (new chains) not all chains start at 0 - :+: v - :+: EpochStartTime t - :+: n - :+: MerkleLogBody (blockHashRecordToVector adjParents) - adjParents = BlockHashRecord $ HM.fromList $ - (\c -> (c, genesisParentBlockHash v c)) <$> HS.toList (adjacentChainIds g p) - --- | This is an expensive call, try not to repeat it. --- -genesisBlockHeaders :: ChainwebVersion -> HM.HashMap ChainId BlockHeader -genesisBlockHeaders Mainnet01 = genesisBlockHeadersMainnet01 -genesisBlockHeaders Testnet04 = genesisBlockHeadersTestnet04 -genesisBlockHeaders Development = genesisBlockHeadersDevelopment -genesisBlockHeaders v = genesisBlockHeaders' v - --- | This is an expensive call, try not to repeat it. --- -genesisBlockHeaders' :: ChainwebVersion -> HM.HashMap ChainId BlockHeader -genesisBlockHeaders' v = HM.fromList - . fmap (id &&& genesisBlockHeaderInternal v) - . toList - $ chainIds v - --- | The set of genesis block headers as it exited at a particular block height --- -genesisBlockHeadersAtHeight - :: ChainwebVersion - -> BlockHeight - -> HM.HashMap ChainId BlockHeader -genesisBlockHeadersAtHeight v h = HM.filter - (\hdr -> _blockHeight hdr <= h) - $ genesisBlockHeaders v - --- -------------------------------------------------------------------------- -- --- Memoize genesis headers for the production networks - -genesisBlockHeadersMainnet01 :: HM.HashMap ChainId BlockHeader -genesisBlockHeadersMainnet01 = genesisBlockHeaders' Mainnet01 -{-# NOINLINE genesisBlockHeadersMainnet01 #-} -genesisBlockHeadersTestnet04 :: HM.HashMap ChainId BlockHeader -genesisBlockHeadersTestnet04 = genesisBlockHeaders' Testnet04 -{-# NOINLINE genesisBlockHeadersTestnet04 #-} -genesisBlockHeadersDevelopment :: HM.HashMap ChainId BlockHeader -genesisBlockHeadersDevelopment = genesisBlockHeaders' Development -{-# NOINLINE genesisBlockHeadersDevelopment #-} - diff --git a/src/Chainweb/BlockHeader/Validation.hs b/src/Chainweb/BlockHeader/Validation.hs index dea675235a..df5c047b0f 100644 --- a/src/Chainweb/BlockHeader/Validation.hs +++ b/src/Chainweb/BlockHeader/Validation.hs @@ -5,6 +5,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} -- | -- Module: Chainweb.BlockHeader.Validation @@ -103,21 +104,18 @@ import qualified Data.Text as T import GHC.Generics -import System.Environment -import System.IO.Unsafe - -- internal modules import Chainweb.BlockCreationTime import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockTarget, genesisParentBlockHash, genesisBlockHeader) import Chainweb.ChainId import Chainweb.ChainValue import Chainweb.Difficulty import Chainweb.Time import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Guards -- -------------------------------------------------------------------------- -- -- Validated BockHeader @@ -675,17 +673,11 @@ validateInductiveWebStep s = concat -- Intrinsic BlockHeader properties -- -------------------------------------------------------------------------- -- -powDisabled :: Bool -powDisabled = case unsafeDupablePerformIO $ lookupEnv "DISABLE_POW_VALIDATION" of - Nothing -> False - Just{} -> True -{-# NOINLINE powDisabled #-} - prop_block_pow :: BlockHeader -> Bool prop_block_pow b | isGenesisBlockHeader b = True - -- Genesis block headers are not mined. So there's not need for POW - | _blockChainwebVersion b == Development && powDisabled = True + -- Genesis block headers are not mined. So there's not need for POW + | b ^. chainwebVersion . versionCheats . disablePow = True | otherwise = checkTarget (_blockTarget b) (_blockPow b) prop_block_hash :: BlockHeader -> Bool @@ -701,18 +693,19 @@ prop_block_genesis_parent b prop_block_genesis_target :: BlockHeader -> Bool prop_block_genesis_target b = isGenesisBlockHeader b - ==> _blockTarget b == genesisBlockTarget (_chainwebVersion b) (_chainId b) + ==> _blockTarget b == _chainwebVersion b ^?! versionGenesis . genesisBlockTarget . onChain (_chainId b) prop_block_current :: Time Micros -> BlockHeader -> Bool prop_block_current t b = BlockCreationTime t >= _blockCreationTime b prop_block_featureFlags :: BlockHeader -> Bool prop_block_featureFlags b - | skipFeatureFlagValidationGuard v h = True + | skipFeatureFlagValidationGuard v cid h = True | otherwise = _blockFlags b == mkFeatureFlags where v = _chainwebVersion b h = _blockHeight b + cid = _chainId b -- | Verify that the adjacent hashes of the block are for the correct set of -- chain ids. @@ -761,7 +754,7 @@ prop_block_target (WebStep as (ChainStep p b)) prop_block_epoch :: WebStep -> Bool prop_block_epoch (WebStep as (ChainStep p b)) - | oldDaGuard (_chainwebVersion b) (_blockHeight b) + | oldDaGuard (_chainwebVersion b) (_chainId b) (_blockHeight b) = _blockEpochStart b <= EpochStartTime (_bct $ _blockCreationTime b) && _blockEpochStart (_parentHeader p) <= _blockEpochStart b && _blockEpochStart b == epochStart p as (_blockCreationTime b) @@ -773,7 +766,7 @@ prop_block_creationTime :: WebStep -> Bool prop_block_creationTime (WebStep as (ChainStep (ParentHeader p) b)) | isGenesisBlockHeader b = _blockCreationTime b == _blockCreationTime p - | oldDaGuard (_chainwebVersion b) (_blockHeight b) + | oldDaGuard (_chainwebVersion b) (_chainId b) (_blockHeight b) = _blockCreationTime b > _blockCreationTime p | otherwise = _blockCreationTime b > _blockCreationTime p diff --git a/src/Chainweb/BlockHeaderDB/Internal.hs b/src/Chainweb/BlockHeaderDB/Internal.hs index 53228717b9..84d934954d 100644 --- a/src/Chainweb/BlockHeaderDB/Internal.hs +++ b/src/Chainweb/BlockHeaderDB/Internal.hs @@ -66,7 +66,6 @@ import qualified Streaming.Prelude as S import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) import Chainweb.BlockHeader.Validation import Chainweb.BlockHeight import Chainweb.ChainId @@ -292,7 +291,7 @@ instance TreeDb BlockHeaderDb where lookup db h = runMaybeT $ do -- lookup rank - r <- MaybeT $ tableLookup (_chainDbRankTable db) h + r <- MaybeT $ tableLookup (_chainDbRankTable db) h MaybeT $ lookupRanked db (int r) h {-# INLINEABLE lookup #-} @@ -402,6 +401,6 @@ insertBlockHeaderDb db = dbAddChecked db . _validatedHeader {-# INLINE insertBlockHeaderDb #-} unsafeInsertBlockHeaderDb :: BlockHeaderDb -> BlockHeader -> IO () -unsafeInsertBlockHeaderDb = dbAddChecked +unsafeInsertBlockHeaderDb = dbAddChecked {-# INLINE unsafeInsertBlockHeaderDb #-} diff --git a/src/Chainweb/BlockHeaderDB/PruneForks.hs b/src/Chainweb/BlockHeaderDB/PruneForks.hs index ad8ed6d2e7..5c966365d6 100644 --- a/src/Chainweb/BlockHeaderDB/PruneForks.hs +++ b/src/Chainweb/BlockHeaderDB/PruneForks.hs @@ -221,8 +221,8 @@ pruneForks_ logg cdb mar mir callback = do deleteHdr k = do -- TODO: make this atomic (create boilerplate to combine queries for -- different tables) - casDelete (_chainDbCas cdb) (RankedBlockHeader k) - tableDelete (_chainDbRankTable cdb) (_blockHash k) + casDelete (_chainDbCas cdb) (RankedBlockHeader k) + tableDelete (_chainDbRankTable cdb) (_blockHash k) logg Debug $ "pruned block header " <> encodeToText (_blockHash k) <> " at height " <> sshow (_blockHeight k) diff --git a/src/Chainweb/BlockHeaderDB/RestAPI/Client.hs b/src/Chainweb/BlockHeaderDB/RestAPI/Client.hs index 68a1042a74..a9a99a0ce0 100644 --- a/src/Chainweb/BlockHeaderDB/RestAPI/Client.hs +++ b/src/Chainweb/BlockHeaderDB/RestAPI/Client.hs @@ -100,7 +100,7 @@ headerClientJson -> DbKey BlockHeaderDb -> ClientM BlockHeader headerClientJson v c k = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ headerClientContentType_ @v @c @JSON k @@ -110,7 +110,7 @@ headerClientJsonPretty -> BlockHash -> ClientM BlockHeader headerClientJsonPretty v c k = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ headerClientContentType_ @v @c @JsonBlockHeaderObject k @@ -120,7 +120,7 @@ headerClientJsonBinary -> BlockHash -> ClientM BlockHeader headerClientJsonBinary v c k = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ headerClientContentType_ @v @c @OctetStream k @@ -187,7 +187,7 @@ headersClientJson -- ^ Filter: no header of `BlockHeight` higher than this will be returned. -> ClientM BlockHeaderPage headersClientJson v c limit start minr maxr = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ headersClientContentType_ @v @c @JSON limit start minr maxr @@ -207,8 +207,7 @@ headersClientJsonPretty -> Maybe MaxRank -- ^ Filter: no header of `BlockHeight` higher than this will be returned. -> ClientM BlockHeaderPage -headersClientJsonPretty v c limit start minr maxr = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v +headersClientJsonPretty (FromSingChainwebVersion (SChainwebVersion :: Sing v)) c limit start minr maxr = runIdentity $ do (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ headersClientContentType_ @v @c @JsonBlockHeaderObject limit start minr maxr @@ -237,8 +236,8 @@ branchHashesClient -> BranchBounds BlockHeaderDb -> ClientM BlockHashPage branchHashesClient v c limit start minr maxr bounds = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v - (SomeSing (SChainId :: Sing c)) <- return $ toSing c + SomeSing (SChainwebVersion :: Sing v) <- return $ toSing (_versionName v) + SomeSing (SChainId :: Sing c) <- return $ toSing c return $ branchHashesClient_ @v @c limit start minr maxr bounds -- -------------------------------------------------------------------------- -- @@ -292,8 +291,8 @@ branchHeadersClientJson -> BranchBounds BlockHeaderDb -> ClientM BlockHeaderPage branchHeadersClientJson v c limit start minr maxr bounds = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v - (SomeSing (SChainId :: Sing c)) <- return $ toSing c + SomeSing (SChainwebVersion :: Sing v) <- return $ toSing (_versionName v) + SomeSing (SChainId :: Sing c) <- return $ toSing c return $ branchHeadersClientContentType_ @v @c @JSON limit start minr maxr bounds branchHeadersClientJsonPretty @@ -306,8 +305,8 @@ branchHeadersClientJsonPretty -> BranchBounds BlockHeaderDb -> ClientM BlockHeaderPage branchHeadersClientJsonPretty v c limit start minr maxr bounds = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v - (SomeSing (SChainId :: Sing c)) <- return $ toSing c + SomeSing (SChainwebVersion :: Sing v) <- return $ toSing (_versionName v) + SomeSing (SChainId :: Sing c) <- return $ toSing c return $ branchHeadersClientContentType_ @v @c @JsonBlockHeaderObject limit start minr maxr bounds -- -------------------------------------------------------------------------- -- @@ -333,6 +332,6 @@ hashesClient -> Maybe MaxRank -> ClientM BlockHashPage hashesClient v c limit start minr maxr = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ hashesClient_ @v @c limit start minr maxr diff --git a/src/Chainweb/BlockHeaderDB/RestAPI/Server.hs b/src/Chainweb/BlockHeaderDB/RestAPI/Server.hs index 77713312ed..8ebf093424 100644 --- a/src/Chainweb/BlockHeaderDB/RestAPI/Server.hs +++ b/src/Chainweb/BlockHeaderDB/RestAPI/Server.hs @@ -73,7 +73,7 @@ import Chainweb.TreeDB import Chainweb.Utils.Paging import Chainweb.Version -import Chainweb.Storage.Table +import Chainweb.Storage.Table -- -------------------------------------------------------------------------- -- -- Handler Tools diff --git a/src/Chainweb/BlockHeight.hs b/src/Chainweb/BlockHeight.hs index 092fd27a2f..a9f1dbad0a 100644 --- a/src/Chainweb/BlockHeight.hs +++ b/src/Chainweb/BlockHeight.hs @@ -55,7 +55,7 @@ import Numeric.Additive -- -------------------------------------------------------------------------- -- -- | BlockHeight -- -newtype BlockHeight = BlockHeight { _height :: Word64 } +newtype BlockHeight = BlockHeight { getBlockHeight :: Word64 } deriving (Eq, Ord, Generic) deriving anyclass (NFData) deriving newtype @@ -101,7 +101,7 @@ decodeBlockHeightBe = BlockHeight <$> getWord64be -- -------------------------------------------------------------------------- -- -- Cut Height -newtype CutHeight = CutHeight Word64 +newtype CutHeight = CutHeight { getCutHeight :: Word64 } deriving (Eq, Ord, Generic) deriving anyclass (NFData) deriving newtype diff --git a/src/Chainweb/ChainId.hs b/src/Chainweb/ChainId.hs index a045ea725d..e313d62d80 100644 --- a/src/Chainweb/ChainId.hs +++ b/src/Chainweb/ChainId.hs @@ -1,13 +1,19 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveFoldable #-} +{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} @@ -50,15 +56,23 @@ module Chainweb.ChainId -- * Testing , unsafeChainId , chainIdInt + +, ChainMap(..) +, onChain +, onChains +, chainZip ) where +import Control.Applicative import Control.DeepSeq -import Control.Lens +import Control.Lens hiding ((.=)) import Control.Monad.Catch (Exception, MonadThrow) import Data.Aeson import Data.Aeson.Types (toJSONKeyText) import Data.Hashable (Hashable(..)) +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HM import Data.Kind import Data.Proxy import qualified Data.Text as T @@ -252,3 +266,33 @@ chainIdInt :: Integral i => ChainId -> i chainIdInt (ChainId cid) = int cid {-# INLINE chainIdInt #-} +-- edtodo: document +data ChainMap a = AllChains a | OnChains (HashMap ChainId a) + deriving stock (Eq, Functor, Foldable, Generic, Ord, Show) + deriving anyclass (Hashable, NFData) + +onChains :: [(ChainId, a)] -> ChainMap a +onChains = OnChains . HM.fromList + +chainZip :: (a -> a -> a) -> ChainMap a -> ChainMap a -> ChainMap a +chainZip f (OnChains l) (OnChains r) = OnChains $ HM.unionWith f l r +chainZip f (OnChains l) (AllChains r) = OnChains $ fmap (`f` r) l +chainZip f (AllChains l) (OnChains r) = OnChains $ fmap (l `f`) r +chainZip f (AllChains l) (AllChains r) = AllChains $ f l r + +instance ToJSON a => ToJSON (ChainMap a) where + toJSON (AllChains a) = object + [ "allChains" .= a + ] + toJSON (OnChains m) = toJSON m + +instance FromJSON a => FromJSON (ChainMap a) where + parseJSON = withObject "ChainMap" $ \o -> + (AllChains <$> o .: "allChains") <|> OnChains <$> parseJSON (Object o) + +makePrisms ''ChainMap + +onChain :: ChainId -> Fold (ChainMap a) a +onChain cid = folding $ \case + OnChains m -> m ^. at cid + AllChains a -> Just a diff --git a/src/Chainweb/Chainweb.hs b/src/Chainweb/Chainweb.hs index 402e93d3d0..4c3a3827f8 100644 --- a/src/Chainweb/Chainweb.hs +++ b/src/Chainweb/Chainweb.hs @@ -256,7 +256,7 @@ withChainweb c logger rocksDb pactDbDir backupDir resetDb inner = confWithBootstraps | _p2pConfigIgnoreBootstrapNodes (_configP2p c) = c | otherwise = configP2p . p2pConfigKnownPeers - %~ (\x -> L.nub $ x <> bootstrapPeerInfos v) $ c + %~ (\x -> L.nub $ x <> _versionBootstraps v) $ c -- TODO: The type InMempoolConfig contains parameters that should be -- configurable as well as parameters that are determined by the chainweb @@ -280,7 +280,7 @@ validatingMempoolConfig cid v gl gp mv = Mempool.InMemConfig , Mempool._inmemCurrentTxsSize = currentTxsSize } where - txcfg = Mempool.chainwebTransactionConfig Nothing + txcfg = Mempool.chainwebTransactionConfig (maxBound :: PactParserVersion) -- The mempool doesn't provide a chain context to the codec which means -- that the latest version of the parser is used. @@ -325,11 +325,11 @@ validatingMempoolConfig cid v gl gp mv = Mempool.InMemConfig let !pay = payloadObj . P._cmdPayload $ tx pcid = P._pmChainId $ P._pMeta pay sigs = length (P._cmdSigs tx) - ver = P._pNetworkId pay >>= fromText @ChainwebVersion . P._networkId + ver = P._pNetworkId pay >>= fromText @ChainwebVersionName . P._networkId tcid <- note (Mempool.InsertErrorOther "Unparsable ChainId") $ fromPactChainId pcid if | tcid /= cid -> Left Mempool.InsertErrorMetadataMismatch | sigs > 100 -> Left $ Mempool.InsertErrorOther "Too many signatures" - | ver /= Just v -> Left Mempool.InsertErrorMetadataMismatch + | ver /= Just (_versionName v) -> Left Mempool.InsertErrorMetadataMismatch | otherwise -> Right tx data StartedChainweb logger @@ -389,7 +389,7 @@ withChainwebInternal conf logger peer serviceSock rocksDb pactDbDir backupDir re (\cid x -> do let mcfg = validatingMempoolConfig cid v (_configBlockGasLimit conf) (_configMinGasPrice conf) -- NOTE: the gas limit may be set based on block height in future, so this approach may not be valid. - let maxGasLimit = fromIntegral <$> maxBlockGasLimit v cid maxBound + let maxGasLimit = fromIntegral <$> maxBlockGasLimit v maxBound when (Just (_configBlockGasLimit conf) > maxGasLimit) $ logg Warn "configured block gas limit is greater than the maximum for this chain; the maximum will be used instead" withChainResources @@ -828,15 +828,9 @@ runChainweb cw = do mempoolSyncClients :: IO [IO ()] mempoolSyncClients = case enabledConfig mempoolP2pConfig of Nothing -> disabled - Just c -> case _chainwebVersion cw of - Test{} -> disabled - TimedConsensus{} -> disabled - PowConsensus{} -> disabled - TimedCPM{} -> enabled c - FastTimedCPM{} -> enabled c - Development -> enabled c - Testnet04 -> enabled c - Mainnet01 -> enabled c + Just c + | cw ^. chainwebVersion . versionCheats . disableMempool -> disabled + | otherwise -> enabled c where disabled = do logg Info "Mempool p2p sync disabled" diff --git a/src/Chainweb/Chainweb/ChainResources.hs b/src/Chainweb/Chainweb/ChainResources.hs index e9f7ae9a84..78f4b2f8e2 100644 --- a/src/Chainweb/Chainweb/ChainResources.hs +++ b/src/Chainweb/Chainweb/ChainResources.hs @@ -110,13 +110,6 @@ withChainResources , _chainResPact = pex } where - pes requestQ = case v of - Test{} -> emptyPactExecutionService - TimedConsensus{} -> emptyPactExecutionService - PowConsensus{} -> emptyPactExecutionService - TimedCPM{} -> mkPactExecutionService requestQ - FastTimedCPM{} -> mkPactExecutionService requestQ - Development -> mkPactExecutionService requestQ - Testnet04 -> mkPactExecutionService requestQ - Mainnet01 -> mkPactExecutionService requestQ - + pes requestQ + | v ^. versionCheats . disablePact = emptyPactExecutionService + | otherwise = mkPactExecutionService requestQ diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 06caa16b60..e82252ab7f 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -5,8 +5,11 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} -- | -- Module: Chainweb.Chainweb.Configuration @@ -85,6 +88,8 @@ import Control.Monad.Except import Control.Monad.Writer import Data.Foldable +import qualified Data.HashMap.Strict as HM +import qualified Data.HashSet as HS import Data.Maybe import qualified Data.Text as T @@ -101,6 +106,7 @@ import System.Directory -- internal modules import Chainweb.BlockHeight +import Chainweb.Difficulty import Chainweb.HostAddress import qualified Chainweb.Mempool.Mempool as Mempool import Chainweb.Mempool.P2pConfig @@ -109,6 +115,9 @@ import Chainweb.Pact.Types (defaultReorgLimit, defaultModuleCacheLimit) import Chainweb.Payload.RestAPI (PayloadBatchLimit(..), defaultServicePayloadBatchLimit) import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Registry import P2P.Node.Configuration import Chainweb.Pact.Backend.DbCache (DbCacheLimitBytes) @@ -407,10 +416,8 @@ validateChainwebConfiguration :: ConfigValidation ChainwebConfiguration [] validateChainwebConfiguration c = do validateMinerConfig (_configMining c) validateBackupConfig (_configBackup c) - case _configChainwebVersion c of - Mainnet01 -> validateP2pConfiguration (_configP2p c) - Testnet04 -> validateP2pConfiguration (_configP2p c) - _ -> return () + unless (c ^. chainwebVersion . versionCheats . disablePeerValidation) $ + validateP2pConfiguration (_configP2p c) validateBackupConfig :: ConfigValidation BackupConfig [] validateBackupConfig c = @@ -447,7 +454,7 @@ defaultChainwebConfiguration v = ChainwebConfiguration instance ToJSON ChainwebConfiguration where toJSON o = object - [ "chainwebVersion" .= _configChainwebVersion o + [ "chainwebVersion" .= _versionName (_configChainwebVersion o) , "cuts" .= _configCuts o , "mining" .= _configMining o , "headerStream" .= _configHeaderStream o @@ -471,40 +478,37 @@ instance ToJSON ChainwebConfiguration where ] instance FromJSON ChainwebConfiguration where - parseJSON = withObject "ChainwebConfiguration" $ \o -> do - v <- o .: "chainwebVersion" .!= Mainnet01 - ($ defaultChainwebConfiguration v) <$> parseJSON (Object o) + parseJSON = fmap ($ defaultChainwebConfiguration Mainnet01) . parseJSON instance FromJSON (ChainwebConfiguration -> ChainwebConfiguration) where - parseJSON = withObject "ChainwebConfig" $ \o -> id - <$< configChainwebVersion ..: "chainwebVersion" % o - <*< configCuts %.: "cuts" % o - <*< configMining %.: "mining" % o - <*< configHeaderStream ..: "headerStream" % o - <*< configReintroTxs ..: "reintroTxs" % o - <*< configP2p %.: "p2p" % o - <*< configThrottling %.: "throttling" % o - <*< configMempoolP2p %.: "mempoolP2p" % o - <*< configBlockGasLimit ..: "gasLimitOfBlock" % o - <*< configLogGas ..: "logGas" % o - <*< configMinGasPrice ..: "minGasPrice" % o - <*< configPactQueueSize ..: "pactQueueSize" % o - <*< configReorgLimit ..: "reorgLimit" % o - <*< configValidateHashesOnReplay ..: "validateHashesOnReplay" % o - <*< configAllowReadsInLocal ..: "allowReadsInLocal" % o - <*< configRosetta ..: "rosetta" % o - <*< configServiceApi %.: "serviceApi" % o - <*< configOnlySyncPact ..: "onlySyncPact" % o - <*< configSyncPactChains ..: "syncPactChains" % o - <*< configBackup %.: "backup" % o - <*< configModuleCacheLimit ..: "moduleCacheLimit" % o + parseJSON = withObject "ChainwebConfiguration" $ \o -> do + id + <$< setProperty configChainwebVersion "chainwebVersion" + (findKnownVersion <=< parseJSON) o + <*< configCuts %.: "cuts" % o + <*< configMining %.: "mining" % o + <*< configHeaderStream ..: "headerStream" % o + <*< configReintroTxs ..: "reintroTxs" % o + <*< configP2p %.: "p2p" % o + <*< configThrottling %.: "throttling" % o + <*< configMempoolP2p %.: "mempoolP2p" % o + <*< configBlockGasLimit ..: "gasLimitOfBlock" % o + <*< configLogGas ..: "logGas" % o + <*< configMinGasPrice ..: "minGasPrice" % o + <*< configPactQueueSize ..: "pactQueueSize" % o + <*< configReorgLimit ..: "reorgLimit" % o + <*< configValidateHashesOnReplay ..: "validateHashesOnReplay" % o + <*< configAllowReadsInLocal ..: "allowReadsInLocal" % o + <*< configRosetta ..: "rosetta" % o + <*< configServiceApi %.: "serviceApi" % o + <*< configOnlySyncPact ..: "onlySyncPact" % o + <*< configSyncPactChains ..: "syncPactChains" % o + <*< configBackup %.: "backup" % o + <*< configModuleCacheLimit ..: "moduleCacheLimit" % o pChainwebConfiguration :: MParser ChainwebConfiguration pChainwebConfiguration = id - <$< configChainwebVersion .:: textOption - % long "chainweb-version" - <> short 'v' - <> help "the chainweb version that this node is using" + <$< configChainwebVersion %:: version <*< configHeaderStream .:: boolOption_ % long "header-stream" <> help "whether to enable an endpoint for streaming block updates" @@ -555,4 +559,37 @@ pChainwebConfiguration = id % long "module-cache-limit" <> help "Maximum size of the per-chain checkpointer module cache in bytes" <> metavar "INT" + where + knownVersion = do + option (findKnownVersion =<< textReader) + % long "chainweb-version" + <> short 'v' + <> help "the chainweb version that this node is using" + version = constructVersion + <$> optional knownVersion + <*> optional (textOption @Fork (long "fork-upper-bound" <> help "(development mode only) the latest fork the node will enable")) + <*> optional (BlockRate <$> textOption (long "block-rate" <> help "(development mode only) the block rate in seconds per block")) + where + constructVersion cliVersion fub br oldVersion + | _versionCode winningVersion == _versionCode devnet = winningVersion + { _versionBlockRate = fromMaybe (_versionBlockRate winningVersion) br + , _versionForks = + maybe (_versionForks winningVersion) (\fub' -> + HM.filterWithKey (\fork _ -> fork <= fub') (_versionForks winningVersion) + ) fub + , _versionUpgrades = + maybe (_versionUpgrades winningVersion) (\fub' -> + OnChains $ HM.mapWithKey + (\cid _ -> + let fubHeight = winningVersion ^?! versionForks . at fub' . _Just . onChain cid + in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) + (HS.toMap (chainIds winningVersion)) + ) fub + } + | Nothing <- br, Nothing <- fub = winningVersion + | otherwise = error + $ "Specifying block-rate or fork-upper-bound is only legal with chainweb-version " + <> "set to development, but version is set to " <> show (_versionName winningVersion) + where + winningVersion = fromMaybe oldVersion cliVersion diff --git a/src/Chainweb/Chainweb/MinerResources.hs b/src/Chainweb/Chainweb/MinerResources.hs index a6442d6db5..e4201b818f 100644 --- a/src/Chainweb/Chainweb/MinerResources.hs +++ b/src/Chainweb/Chainweb/MinerResources.hs @@ -275,7 +275,7 @@ runMiner v mr | enabled = case _minerResCoordination mr of Nothing -> error "Mining coordination must be enabled in order to use the in-node test miner" - Just coord -> case window v of + Just coord -> case _versionWindow v of Nothing -> testMiner coord Just _ -> powMiner coord | otherwise = mempoolNoopMiner lf (_chainResMempool <$> _minerChainResources mr) @@ -296,4 +296,4 @@ runMiner v mr gen <- MWC.createSystemRandom localTest lf v coord (_nodeMiner conf) cdb gen (_nodeTestMiners conf) - powMiner coord = localPOW lf v coord (_nodeMiner conf) cdb + powMiner coord = localPOW lf coord (_nodeMiner conf) cdb diff --git a/src/Chainweb/Chainweb/PeerResources.hs b/src/Chainweb/Chainweb/PeerResources.hs index 70f0d856c1..b269263bdd 100644 --- a/src/Chainweb/Chainweb/PeerResources.hs +++ b/src/Chainweb/Chainweb/PeerResources.hs @@ -213,7 +213,7 @@ getHost -> IO (Either T.Text Hostname) getHost mgr ver logger peers = do nis <- forConcurrently peers $ \p -> - tryAllSynchronous (requestRemoteNodeInfo mgr ver (_peerAddr p) Nothing) >>= \case + tryAllSynchronous (requestRemoteNodeInfo mgr (_versionName ver) (_peerAddr p) Nothing) >>= \case Right x -> Just x <$ do logFunctionText logger Info $ "got remote info from " <> toText (_peerAddr p) @@ -244,7 +244,7 @@ withPeerSocket conf act = withSocket port interface $ \(p, s) -> -- Run PeerDb for a Chainweb Version startPeerDb_ :: ChainwebVersion -> P2pConfiguration -> IO PeerDb -startPeerDb_ v = startPeerDb nids +startPeerDb_ v = startPeerDb v nids where nids = HS.singleton CutNetwork `HS.union` HS.map MempoolNetwork cids diff --git a/src/Chainweb/Crypto/MerkleLog.hs b/src/Chainweb/Crypto/MerkleLog.hs index 53377ea944..64c18df8ba 100644 --- a/src/Chainweb/Crypto/MerkleLog.hs +++ b/src/Chainweb/Crypto/MerkleLog.hs @@ -211,9 +211,6 @@ expectedTreeNodeException = MerkleLogWrongNodeTypeException -- -------------------------------------------------------------------------- -- -- Internal Utils -uncurry3 :: (t1 -> t2 -> t3 -> t4) -> (t1, t2, t3) -> t4 -uncurry3 f (a,b,c) = f a b c - fromWordBE :: forall w b . BA.ByteArray b => ByteSwap w => w -> b fromWordBE w = BA.allocAndFreeze (sizeOf (undefined :: w)) $ \ptr -> poke ptr (BA.toBE w) diff --git a/src/Chainweb/Cut.hs b/src/Chainweb/Cut.hs index b470d91a37..c4e03d4d4a 100644 --- a/src/Chainweb/Cut.hs +++ b/src/Chainweb/Cut.hs @@ -124,7 +124,6 @@ import qualified Streaming.Prelude as S import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeight import Chainweb.BlockWeight import Chainweb.ChainId diff --git a/src/Chainweb/Cut/Create.hs b/src/Chainweb/Cut/Create.hs index 39140c3c9e..042da19b76 100644 --- a/src/Chainweb/Cut/Create.hs +++ b/src/Chainweb/Cut/Create.hs @@ -82,10 +82,12 @@ import Chainweb.BlockHash import Chainweb.BlockHeader import Chainweb.BlockHeader.Validation import Chainweb.BlockHeight +import Chainweb.ChainId import Chainweb.ChainValue import Chainweb.Cut import Chainweb.Cut.CutHashes import Chainweb.Difficulty +import Chainweb.Graph import Chainweb.Payload import Chainweb.Time import Chainweb.Utils diff --git a/src/Chainweb/Cut/CutHashes.hs b/src/Chainweb/Cut/CutHashes.hs index 0b3877c78f..7fcee36119 100644 --- a/src/Chainweb/Cut/CutHashes.hs +++ b/src/Chainweb/Cut/CutHashes.hs @@ -100,6 +100,7 @@ import Chainweb.Cut import Chainweb.Utils import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Registry import Chainweb.Payload @@ -318,7 +319,7 @@ cutHashesProperties c = , "origin" .= _cutOrigin c , "weight" .= _cutHashesWeight c , "height" .= _cutHashesHeight c - , "instance" .= _cutHashesChainwebVersion c + , "instance" .= _versionCode (_cutHashesChainwebVersion c) , "id" .= _cutHashesId c ] <> ifNotEmpty "headers" cutHashesHeaders @@ -346,7 +347,7 @@ instance FromJSON CutHashes where <*> o .: "origin" <*> o .: "weight" <*> o .: "height" - <*> o .: "instance" + <*> (lookupVersionByCode <$> o .: "instance") <*> o .: "id" <*> o .:? "headers" .!= mempty <*> o .:? "payloads" .!= mempty diff --git a/src/Chainweb/CutDB.hs b/src/Chainweb/CutDB.hs index 814c03a51d..6dd2aa67bd 100644 --- a/src/Chainweb/CutDB.hs +++ b/src/Chainweb/CutDB.hs @@ -821,7 +821,7 @@ member db cid h = do -- -------------------------------------------------------------------------- -- -- Some CutDB --- | 'CutDb' with type level 'ChainwebVersion' +-- | 'CutDb' with type level 'ChainwebVersionTag -- newtype CutDbT tbl (v :: ChainwebVersionT) = CutDbT (CutDb tbl) deriving (Generic) diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index 0b98a61bf9..8fda070b9f 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -28,9 +28,10 @@ -- target value divided by the hash target for the block. -- module Chainweb.Difficulty -( +( BlockRate(..) +, WindowWidth(..) -- * PowHashNat - PowHashNat(..) +, PowHashNat(..) , powHashNat , encodePowHashNat , decodePowHashNat @@ -84,18 +85,34 @@ import Text.Printf (printf) import Chainweb.Crypto.MerkleLog import Chainweb.MerkleUniverse import Chainweb.PowHash -import Chainweb.Time (Micros(..), Seconds, TimeSpan(..)) +import Chainweb.Time (Micros(..), TimeSpan(..)) import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version import Numeric.Additive +import Numeric.Natural -- -------------------------------------------------------------------------- -- -- Large Word Orphans instance NFData Word128 instance NFData Word256 +-- +-- -- | The gap in SECONDS that we desire between the Creation Time of subsequent +-- -- blocks in some chain. +-- -- +newtype BlockRate = BlockRate { _getBlockRate :: Micros } + deriving stock (Eq, Generic, Ord, Show) + deriving newtype (Hashable, NFData, ToJSON, FromJSON) + +-- -- | The number of blocks to be mined after a difficulty adjustment, before +-- -- considering a further adjustment. Critical for the "epoch-based" adjustment +-- -- algorithm seen in `adjust`. +-- -- +newtype WindowWidth = WindowWidth Natural + deriving stock (Eq, Generic, Ord, Show) + deriving newtype (Hashable, NFData, ToJSON, FromJSON) + -- -------------------------------------------------------------------------- -- -- PowHashNat @@ -283,10 +300,8 @@ targetToDifficulty (HashTarget (PowHashNat target)) = -- smaller or equal than 2^256-1. -- adjust - :: ChainwebVersion - -- ^ Chainweb Version + :: BlockRate -> WindowWidth - -- ^ The number of blocks in an epoch -> TimeSpan Micros -- ^ the actual time of the last epoch: creation time minus the epoch -- start time of the last block in the (previous) epoch @@ -295,15 +310,10 @@ adjust -- the last header in the (previous) epoch -> HashTarget -- ^ the hash target of the new epoch -adjust v (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = newTarget +adjust (BlockRate br) (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = newTarget where - br :: Seconds - br = _getBlockRate $ blockRate v - - -- TODO: the block rate should be specified in microseconds in - -- "Chainweb.Version". targetedEpochTime :: Rational - targetedEpochTime = int ww * int br * 1000000 + targetedEpochTime = int ww * int br actualEpochTime :: Rational actualEpochTime = int delta @@ -320,10 +330,8 @@ adjust v (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = newTarget -- This is used when 'oldDaGuard' is active. -- legacyAdjust - :: ChainwebVersion - -- ^ Chainweb Version + :: BlockRate -> WindowWidth - -- ^ The number of blocks in an epoch -> TimeSpan Micros -- ^ the actual time of the last epoch: creation time minus the epoch -- start time of the last block in the (previous) epoch @@ -332,13 +340,10 @@ legacyAdjust -- the last header in the (previous) epoch -> HashTarget -- ^ the hash target of the new epoch -legacyAdjust v (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = newTarget +legacyAdjust (BlockRate br) (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = newTarget where - br :: Seconds - br = _getBlockRate $ blockRate v - newDiff :: Rational - newDiff = oldDiff * int br * int ww * 1000000 / int delta + newDiff = oldDiff * int br * int ww / int delta oldDiff :: Rational oldDiff = int maxTargetWord / int oldTarget diff --git a/src/Chainweb/Graph.hs b/src/Chainweb/Graph.hs index 2f0b69ca60..5f02ecae06 100644 --- a/src/Chainweb/Graph.hs +++ b/src/Chainweb/Graph.hs @@ -327,7 +327,7 @@ checkAdjacentChainIds g cid expectedAdj = do -- | Graphs which have known, specific, intended meaning for Chainweb. -- data KnownGraph = Singleton | Pair | Triangle | Peterson | Twenty | HoffmanSingleton - deriving (Generic) + deriving (Generic, Enum, Bounded) deriving anyclass (NFData) instance HasTextRepresentation KnownGraph where diff --git a/src/Chainweb/Mempool/Consensus.hs b/src/Chainweb/Mempool/Consensus.hs index c24499ec60..b27fce61ed 100644 --- a/src/Chainweb/Mempool/Consensus.hs +++ b/src/Chainweb/Mempool/Consensus.hs @@ -42,7 +42,7 @@ import System.LogLevel ------------------------------------------------------------------------------ import Chainweb.BlockHeader import Chainweb.BlockHeaderDB -import Chainweb.BlockHeight +import Chainweb.ChainId import Chainweb.Mempool.InMem import Chainweb.Mempool.Mempool import Chainweb.Payload @@ -52,6 +52,7 @@ import Chainweb.Transaction import Chainweb.TreeDB import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Guards import Chainweb.Storage.Table import Data.LogMessage (JsonLog(..), LogFunction) @@ -108,22 +109,23 @@ processFork processFork blockHeaderDb payloadStore lastHeaderRef logFun newHeader = do now <- getCurrentTimeIntegral lastHeader <- readIORef lastHeaderRef - let v = _chainwebVersion newHeader + let v = _chainwebVersion blockHeaderDb + cid = _chainId blockHeaderDb height = _blockHeight newHeader (a, b) <- processFork' logFun blockHeaderDb newHeader lastHeader (payloadLookup payloadStore) - (processForkCheckTTL (Just (v, height)) now) + (processForkCheckTTL (pactParserVersion v cid height) now) return (V.map unHashable a, V.map unHashable b) ------------------------------------------------------------------------------ processForkCheckTTL - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> Time Micros -> HashableTrans PayloadWithText -> Bool -processForkCheckTTL chainCtx now (HashableTrans t) = +processForkCheckTTL ppv now (HashableTrans t) = either (const False) (const True) $ - txTTLCheck (chainwebTransactionConfig chainCtx) now t + txTTLCheck (chainwebTransactionConfig ppv) now t ------------------------------------------------------------------------------ @@ -178,7 +180,7 @@ payloadLookup payloadStore bh = Nothing -> return mempty Just s -> do pd <- casLookupM' (view transactionDb s) (_blockPayloadHash bh) - chainwebTxsFromPd (Just (_chainwebVersion bh, _blockHeight bh)) pd + chainwebTxsFromPd (pactParserVersion (_chainwebVersion bh) (_chainId bh) (_blockHeight bh)) pd where casLookupM' s h = do x <- tableLookup s h @@ -189,10 +191,10 @@ payloadLookup payloadStore bh = ------------------------------------------------------------------------------ chainwebTxsFromPd - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> PayloadData -> IO (HashSet (HashableTrans PayloadWithText)) -chainwebTxsFromPd chainCtx pd = do +chainwebTxsFromPd ppv pd = do let transSeq = _payloadDataTransactions pd let bytes = _transactionBytes <$> transSeq let eithers = toCWTransaction <$> bytes @@ -201,4 +203,4 @@ chainwebTxsFromPd chainCtx pd = do let theRights = rights $ toList eithers return $! HS.fromList $ HashableTrans <$!> theRights where - toCWTransaction = codecDecode (chainwebPayloadCodec chainCtx) + toCWTransaction = codecDecode (chainwebPayloadCodec ppv) diff --git a/src/Chainweb/Mempool/Mempool.hs b/src/Chainweb/Mempool/Mempool.hs index c0c3db3d29..1670cbdbc5 100644 --- a/src/Chainweb/Mempool/Mempool.hs +++ b/src/Chainweb/Mempool/Mempool.hs @@ -140,7 +140,6 @@ import qualified Chainweb.Time as Time import Chainweb.Transaction import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version (ChainwebVersion(..)) import Data.LogMessage (LogFunctionText) ------------------------------------------------------------------------------ @@ -359,9 +358,9 @@ noopMempool = do ------------------------------------------------------------------------------ chainwebTransactionConfig - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> TransactionConfig ChainwebTransaction -chainwebTransactionConfig chainCtx = TransactionConfig (chainwebPayloadCodec chainCtx) +chainwebTransactionConfig ppv = TransactionConfig (chainwebPayloadCodec ppv) commandHash chainwebTestHashMeta getGasPrice diff --git a/src/Chainweb/Miner/Core.hs b/src/Chainweb/Miner/Core.hs index d2b4db36f0..dd1e726c9f 100644 --- a/src/Chainweb/Miner/Core.hs +++ b/src/Chainweb/Miner/Core.hs @@ -20,19 +20,16 @@ module Chainweb.Miner.Core , TargetBytes(..) , ChainBytes(..) , WorkBytes(..) - , usePowHash , mine ) where import Control.Monad -import Crypto.Hash.Algorithms (Blake2s_256) import Crypto.Hash.IO import qualified Data.ByteArray as BA import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Short as BS -import Data.Proxy (Proxy(..)) import Data.Word (Word64, Word8) import Foreign.Marshal.Alloc (allocaBytes) @@ -49,7 +46,6 @@ import Chainweb.Difficulty import Chainweb.Time hiding (second) import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version (ChainwebVersion(..)) --- @@ -76,18 +72,6 @@ newtype ChainBytes = ChainBytes { _chainBytes :: B.ByteString } deriving stock (Eq, Show) deriving newtype (MimeRender OctetStream, MimeUnrender OctetStream) --- | Select a hashing algorithm. --- -usePowHash :: ChainwebVersion -> (forall a. HashAlgorithm a => Proxy a -> f) -> f -usePowHash Test{} f = f $ Proxy @Blake2s_256 -usePowHash TimedConsensus{} f = f $ Proxy @Blake2s_256 -usePowHash PowConsensus{} f = f $ Proxy @Blake2s_256 -usePowHash TimedCPM{} f = f $ Proxy @Blake2s_256 -usePowHash FastTimedCPM{} f = f $ Proxy @Blake2s_256 -usePowHash Development f = f $ Proxy @Blake2s_256 -usePowHash Testnet04 f = f $ Proxy @Blake2s_256 -usePowHash Mainnet01 f = f $ Proxy @Blake2s_256 - -- -------------------------------------------------------------------------- -- -- CPU Mining -- diff --git a/src/Chainweb/Miner/Miners.hs b/src/Chainweb/Miner/Miners.hs index 02e34f579a..0a1bd183a1 100644 --- a/src/Chainweb/Miner/Miners.hs +++ b/src/Chainweb/Miner/Miners.hs @@ -36,10 +36,11 @@ import Control.Concurrent.Async (race) import Control.Lens import Control.Monad +import Crypto.Hash.Algorithms (Blake2s_256) + import qualified Data.ByteString.Short as BS import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HashMap -import Data.Proxy import Numeric.Natural (Natural) @@ -53,6 +54,8 @@ import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Cut.Create import Chainweb.CutDB +import Chainweb.Difficulty +import Chainweb.Graph import Chainweb.Logger import Chainweb.Mempool.Mempool import qualified Chainweb.Mempool.Mempool as Mempool @@ -98,7 +101,7 @@ localTest lf v coord m cdb gen miners = void $ awaitNewCut cdb c where meanBlockTime :: Double - meanBlockTime = int $ _getBlockRate $ blockRate v + meanBlockTime = int (_getBlockRate (blockRate v)) / 1_000_000 go :: BlockHeight -> WorkHeader -> IO SolvedWork go height w = do @@ -106,7 +109,7 @@ localTest lf v coord m cdb gen miners = runGetS decodeSolvedWork $ BS.fromShort $ _workHeaderBytes w where t :: Double - t = int graphOrder / (int (_minerCount miners) * meanBlockTime * 1000000) + t = int graphOrder / (int (_minerCount miners) * meanBlockTime * 1_000_000) graphOrder :: Natural graphOrder = order $ chainGraphAt v height @@ -131,12 +134,11 @@ mempoolNoopMiner lf chainRes = localPOW :: Logger logger => LogFunction - -> ChainwebVersion -> MiningCoordination logger tbl -> Miner -> CutDb tbl -> IO () -localPOW lf v coord m cdb = runForever lf "Chainweb.Miner.Miners.localPOW" $ do +localPOW lf coord m cdb = runForever lf "Chainweb.Miner.Miners.localPOW" $ do c <- _cut cdb wh <- work coord Nothing m race (awaitNewCutByChainId cdb (_workHeaderChainId wh) c) (go wh) >>= \case @@ -146,4 +148,4 @@ localPOW lf v coord m cdb = runForever lf "Chainweb.Miner.Miners.localPOW" $ do void $ awaitNewCut cdb c where go :: WorkHeader -> IO SolvedWork - go wh = usePowHash v $ \(_ :: Proxy a) -> mine @a (Nonce 0) wh + go = mine @Blake2s_256 (Nonce 0) diff --git a/src/Chainweb/Miner/RestAPI/Server.hs b/src/Chainweb/Miner/RestAPI/Server.hs index 7040b98939..a47ecff969 100644 --- a/src/Chainweb/Miner/RestAPI/Server.hs +++ b/src/Chainweb/Miner/RestAPI/Server.hs @@ -51,6 +51,7 @@ import System.Random -- internal modules +import Chainweb.ChainId import Chainweb.Cut (Cut) import Chainweb.Cut.Create import Chainweb.CutDB (awaitNewCutByChainIdStm, _cut) diff --git a/src/Chainweb/NodeVersion.hs b/src/Chainweb/NodeVersion.hs index fdc4abd2a5..b2a28409bb 100644 --- a/src/Chainweb/NodeVersion.hs +++ b/src/Chainweb/NodeVersion.hs @@ -101,7 +101,7 @@ requestTimeoutMicros = 4 * 1000000 getNodeVersion :: HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionName -> HostAddress -> Maybe T.Text -> IO (Either T.Text NodeVersion) @@ -125,7 +125,7 @@ getNodeVersion mgr ver addr maybeReq = do Just e -> req ver addr e req - :: ChainwebVersion + :: ChainwebVersionName -> HostAddress -> T.Text -> HTTP.Request @@ -146,7 +146,7 @@ req ver addr endpoint = HTTP.defaultRequest } cutReq - :: ChainwebVersion + :: ChainwebVersionName -> HostAddress -> HTTP.Request cutReq ver addr = HTTP.defaultRequest @@ -218,7 +218,7 @@ instance ToJSON RemoteNodeInfo where -- requestRemoteNodeInfo :: HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionName -> HostAddress -> Maybe T.Text -> IO RemoteNodeInfo diff --git a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs index 1f61186248..44ad22cc37 100644 --- a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs +++ b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs @@ -75,12 +75,13 @@ import Pact.Types.Util (AsString(..)) -- chainweb import Chainweb.BlockHash +import Chainweb.BlockHeader import Chainweb.BlockHeight import Chainweb.Pact.Backend.DbCache import Chainweb.Pact.Backend.Types import Chainweb.Pact.Backend.Utils import Chainweb.Pact.Service.Types (PactException(..), internalError) -import Chainweb.Version (ChainwebVersion, ChainId, genesisHeight) +import Chainweb.Version import Chainweb.Utils (encodeToByteString, sshow) import Chainweb.Utils.Serialization @@ -745,7 +746,7 @@ handlePossibleRewind v cid bRestore hsh = do printf "handlePossibleRewind: The checkpointer attempted to restore to block hash\ \ %s at block height %d, which is not in the current block history of the\ \ checkpointer at height %d." - (blockHashToText hsh) (_height bRestore - 1) (_height succOfCurrent - 1) + (blockHashToText hsh) (getBlockHeight bRestore - 1) (getBlockHeight succOfCurrent - 1) rowCountErrorMessage = T.pack . printf "At this blockheight/blockhash (%d, %s) in BlockHistoryTable, there are %d entries." @@ -756,7 +757,7 @@ handlePossibleRewind v cid bRestore hsh = do printf "handlePossibleRewind: The checkpointer attempted to restore to block hash %s\ \ at height %d, which is greater than the max entry in the block history of the\ \ checkpointer at height %d." - (blockHashToText hsh) (_height bRestore - 1) (_height succOfCurrent - 1) + (blockHashToText hsh) (getBlockHeight bRestore - 1) (getBlockHeight succOfCurrent - 1) newChildBlock bCurrent = do assign bsBlockHeight bRestore diff --git a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs index 7b9d3177d5..efc0244aca 100644 --- a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs +++ b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs @@ -70,6 +70,7 @@ import Chainweb.Pact.Service.Types import Chainweb.Utils import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Guards initRelationalCheckpointer :: BlockState @@ -152,9 +153,9 @@ doRestore v cid dbenv (Just (bh, hash)) = runBlockEnv dbenv $ do return $! PactDbEnv' $! PactDbEnv chainwebPactDb dbenv where -- Module name fix follows the restore call to checkpointer. - setModuleNameFix = bsModuleNameFix .= enableModuleNameFix v bh - setSortedKeys = bsSortedKeys .= pact420Upgrade v bh - setLowerCaseTables = bsLowerCaseTables .= chainweb217Pact After v bh + setModuleNameFix = bsModuleNameFix .= enableModuleNameFix v cid bh + setSortedKeys = bsSortedKeys .= pact420 v cid bh + setLowerCaseTables = bsLowerCaseTables .= chainweb217Pact v cid bh doRestore _ _ dbenv Nothing = runBlockEnv dbenv $ do clearPendingTxState withSavepoint DbTransaction $ diff --git a/src/Chainweb/Pact/Backend/Utils.hs b/src/Chainweb/Pact/Backend/Utils.hs index dae8a25ce9..c9433d5a18 100644 --- a/src/Chainweb/Pact/Backend/Utils.hs +++ b/src/Chainweb/Pact/Backend/Utils.hs @@ -89,11 +89,11 @@ import Pact.Types.Util (AsString(..)) -- chainweb +import Chainweb.ChainId import Chainweb.Logger import Chainweb.Pact.Backend.SQLite.DirectV2 import Chainweb.Pact.Backend.Types import Chainweb.Pact.Service.Types -import Chainweb.Version import Chainweb.Utils -- -------------------------------------------------------------------------- -- diff --git a/src/Chainweb/Pact/PactService.hs b/src/Chainweb/Pact/PactService.hs index 639db82f6b..6c5f9df52e 100644 --- a/src/Chainweb/Pact/PactService.hs +++ b/src/Chainweb/Pact/PactService.hs @@ -29,7 +29,7 @@ module Chainweb.Pact.PactService , execHistoricalLookup , execSyncToBlock , runPactService - , runPactService' + , withPactService , execNewGenesisBlock , getGasModel ) where @@ -71,9 +71,9 @@ import qualified Pact.Types.SPV as P import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockHeader, genesisBlockPayload) import Chainweb.BlockHeaderDB import Chainweb.BlockHeight +import Chainweb.ChainId import Chainweb.Logger import Chainweb.Mempool.Mempool as Mempool import Chainweb.Miner.Pact @@ -92,6 +92,7 @@ import Chainweb.Transaction import Chainweb.TreeDB (lookupM) import Chainweb.Utils hiding (check) import Chainweb.Version +import Chainweb.Version.Guards import Data.LogMessage import Utils.Logging.Trace @@ -109,11 +110,11 @@ runPactService -> PactServiceConfig -> IO () runPactService ver cid chainwebLogger reqQ mempoolAccess bhDb pdb sqlenv config = - void $ runPactService' ver cid chainwebLogger bhDb pdb sqlenv config $ do + void $ withPactService ver cid chainwebLogger bhDb pdb sqlenv config $ do initialPayloadState chainwebLogger mempoolAccess ver cid serviceRequests (logFunction chainwebLogger) mempoolAccess reqQ -runPactService' +withPactService :: Logger logger => CanReadablePayloadCas tbl => ChainwebVersion @@ -125,7 +126,7 @@ runPactService' -> PactServiceConfig -> PactServiceM tbl a -> IO (T2 a PactServiceState) -runPactService' ver cid chainwebLogger bhDb pdb sqlenv config act = +withPactService ver cid chainwebLogger bhDb pdb sqlenv config act = withProdRelationalCheckpointer checkpointerLogger initialBlockState sqlenv cplogger ver cid $ \checkpointEnv -> do let !rs = readRewards !initialParentHeader = ParentHeader $ genesisBlockHeader ver cid @@ -182,19 +183,10 @@ initialPayloadState -> ChainwebVersion -> ChainId -> PactServiceM tbl () -initialPayloadState _ _ Test{} _ = pure () -initialPayloadState _ _ TimedConsensus{} _ = pure () -initialPayloadState _ _ PowConsensus{} _ = pure () -initialPayloadState logger mpa v@TimedCPM{} cid = - initializeCoinContract logger mpa v cid $ genesisBlockPayload v cid -initialPayloadState logger mpa v@FastTimedCPM{} cid = - initializeCoinContract logger mpa v cid $ genesisBlockPayload v cid -initialPayloadState logger mpa v@Development cid = - initializeCoinContract logger mpa v cid $ genesisBlockPayload v cid -initialPayloadState logger mpa v@Testnet04 cid = - initializeCoinContract logger mpa v cid $ genesisBlockPayload v cid -initialPayloadState logger mpa v@Mainnet01 cid = - initializeCoinContract logger mpa v cid $ genesisBlockPayload v cid +initialPayloadState logger mpa v cid + | v ^. versionCheats . disablePact = pure () + | otherwise = initializeCoinContract logger mpa v cid $ + v ^?! versionGenesis . genesisBlockPayload . onChain cid initializeCoinContract :: forall tbl logger. (CanReadablePayloadCas tbl, Logger logger) @@ -207,7 +199,7 @@ initializeCoinContract initializeCoinContract _logger memPoolAccess v cid pwo = do cp <- getCheckpointer genesisExists <- liftIO - $ _cpLookupBlockInCheckpointer cp (genesisHeight v cid, ghash) + $ _cpLookupBlockInCheckpointer cp (_blockHeight genesisHeader, ghash) if genesisExists then readContracts else validateGenesis @@ -630,8 +622,8 @@ execLocal cmd = withDiscardedBatch $ do spv <- use psSpvSupport let execConfig = P.mkExecutionConfig $ [ P.FlagAllowReadInLocal | _psAllowReadsInLocal ] ++ - enablePactEvents' pd ++ - enforceKeysetFormats' pd + enablePactEvents' (ctxVersion pd) (ctxChainId pd) (ctxCurrentBlockHeight pd) ++ + enforceKeysetFormats' (ctxVersion pd) (ctxChainId pd) (ctxCurrentBlockHeight pd) logger = P.newLogger _psLoggers "execLocal" withCurrentCheckpointer "execLocal" $ \(PactDbEnv' pdbenv) -> do r <- liftIO $ @@ -776,5 +768,5 @@ chainweb213GasModel = modifiedGasModel getGasModel :: TxContext -> P.GasModel getGasModel ctx - | chainweb213Pact (ctxVersion ctx) (ctxCurrentBlockHeight ctx) = chainweb213GasModel + | chainweb213Pact (ctxVersion ctx) (ctxChainId ctx) (ctxCurrentBlockHeight ctx) = chainweb213GasModel | otherwise = freeModuleLoadGasModel diff --git a/src/Chainweb/Pact/PactService/ExecBlock.hs b/src/Chainweb/Pact/PactService/ExecBlock.hs index 3cacf06e43..3937c341a6 100644 --- a/src/Chainweb/Pact/PactService/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/ExecBlock.hs @@ -85,6 +85,7 @@ import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils hiding (check) import Chainweb.Version +import Chainweb.Version.Guards -- | Set parent header in state and spv support (using parent hash) setParentHeader :: String -> ParentHeader -> PactServiceM tbl () @@ -118,7 +119,9 @@ execBlock currHeader plData pdbenv = do error $ "Code invariant violation: execBlock must be called with withCheckpointer. Please report this as a bug." miner <- decodeStrictOrThrow' (_minerData $ _payloadDataMiner plData) - trans <- liftIO $ transactionsFromPayload (Just (v, _blockHeight currHeader)) plData + trans <- liftIO $ transactionsFromPayload + (pactParserVersion v (_blockChainId currHeader) (_blockHeight currHeader)) + plData cp <- getCheckpointer logger <- view psLogger @@ -128,10 +131,8 @@ execBlock currHeader plData pdbenv = do -- The new default behavior is to use the creation time of the /parent/ header. -- txValidationTime <- if isGenesisBlockHeader currHeader - then - return (ParentCreationTime $ _blockCreationTime currHeader) - else - ParentCreationTime . _blockCreationTime . _parentHeader <$> use psParentHeader + then return (ParentCreationTime $ _blockCreationTime currHeader) + else ParentCreationTime . _blockCreationTime . _parentHeader <$> use psParentHeader -- prop_tx_ttl_validate valids <- liftIO $ V.zip trans <$> @@ -157,7 +158,7 @@ execBlock currHeader plData pdbenv = do where blockGasLimit = - fromIntegral <$> maxBlockGasLimit v (_blockChainId currHeader) (_blockHeight currHeader) + fromIntegral <$> maxBlockGasLimit v (_blockHeight currHeader) logInitCache = do mc <- fmap (fmap instr) <$> use psInitCache @@ -220,7 +221,7 @@ validateChainwebTxs logger v cid cp txValidationTime bh txs doBuyGas >>= runValid checkTxHash >>= runValid checkTxSigs >>= runValid checkTimes - >>= runValid (return . checkCompile v bh) + >>= runValid (return . checkCompile v cid bh) checkUnique :: ChainwebTransaction -> IO (Either InsertError ChainwebTransaction) checkUnique t = do @@ -231,7 +232,7 @@ validateChainwebTxs logger v cid cp txValidationTime bh txs doBuyGas checkTimes :: ChainwebTransaction -> IO (Either InsertError ChainwebTransaction) checkTimes t - | skipTxTimingValidation v bh = return $ Right t + | skipTxTimingValidation v cid bh = return $ Right t | timingsCheck txValidationTime $ fmap payloadObj t = return $ Right t | otherwise = return $ Left InsertErrorInvalidTime @@ -239,7 +240,7 @@ validateChainwebTxs logger v cid cp txValidationTime bh txs doBuyGas checkTxHash t = case P.verifyHash (P._cmdHash t) (SB.fromShort $ payloadBytes $ P._cmdPayload t) of Left _ - | doCheckTxHash v bh -> return $ Left $ InsertErrorInvalidHash + | doCheckTxHash v cid bh -> return $ Left $ InsertErrorInvalidHash | otherwise -> do P.logLog logger "DEBUG" "ignored legacy tx-hash failure" return $ Right t @@ -249,9 +250,10 @@ validateChainwebTxs logger v cid cp txValidationTime bh txs doBuyGas checkTxSigs t = case validateSigs t of Left _ -- special case for old testnet history - | v == Testnet04 && not (doCheckTxHash v bh) -> do - P.logLog logger "DEBUG" "ignored legacy invalid signature" - return $ Right t + -- edtodo: no more? + -- v == Testnet04 && not (doCheckTxHash v bh) -> do + -- P.logLog logger "DEBUG" "ignored legacy invalid signature" + -- return $ Right t | otherwise -> return $ Left $ InsertErrorInvalidSigs Right _ -> pure $ Right t @@ -283,10 +285,11 @@ type RunGas = ValidateTxs -> IO ValidateTxs checkCompile :: ChainwebVersion + -> ChainId -> BlockHeight -> ChainwebTransaction -> Either InsertError ChainwebTransaction -checkCompile v bh tx = case payload of +checkCompile v cid bh tx = case payload of Exec (ExecMsg parsedCode _) -> case compileCode parsedCode of Left perr -> Left $ InsertErrorCompilationFailed (sshow perr) @@ -295,7 +298,7 @@ checkCompile v bh tx = case payload of where payload = P._pPayload $ payloadObj $ P._cmdPayload tx compileCode p = - let e = ParseEnv (chainweb216Pact After v bh) + let e = ParseEnv (chainweb216Pact v cid bh) in compileExps e (mkTextInfo (P._pcCode p)) (P._pcExps p) skipDebitGas :: RunGas @@ -424,10 +427,11 @@ applyPactCmd isGenesis env miner txTimeLimit cmd = StateT $ \(T2 mcache maybeBlo set cmdGasLimit newTxGasLimit (payloadObj <$> cmd) initialGas = initialGasOf (P._cmdPayload cmd) handle onBuyGasFailure $ do - T2 result mcache' <- if isGenesis + T2 result mcache' <- do + pd <- getTxContext (publicMetaOf gasLimitedCmd) + if isGenesis then liftIO $! applyGenesisCmd logger env P.noSPVSupport gasLimitedCmd else do - pd <- getTxContext (publicMetaOf gasLimitedCmd) spv <- use psSpvSupport let timeoutError = TxTimeout (requestKeyToTransactionHash $ P.cmdToRequestKey cmd) @@ -461,10 +465,10 @@ toHashCommandResult :: P.CommandResult [P.TxLog A.Value] -> P.CommandResult P.Ha toHashCommandResult = over (P.crLogs . _Just) $ P.pactHash . encodeToByteString transactionsFromPayload - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> PayloadData -> IO (Vector ChainwebTransaction) -transactionsFromPayload chainCtx plData = do +transactionsFromPayload ppv plData = do vtrans <- fmap V.fromList $ mapM toCWTransaction $ toList (_payloadDataTransactions plData) @@ -475,7 +479,7 @@ transactionsFromPayload chainCtx plData = do <> T.intercalate ". " ls return $! V.fromList theRights where - toCWTransaction bs = evaluate (force (codecDecode (chainwebPayloadCodec chainCtx) $ + toCWTransaction bs = evaluate (force (codecDecode (chainwebPayloadCodec ppv) $ _transactionBytes bs)) debugResult :: A.ToJSON a => Text -> a -> PactServiceM tbl () diff --git a/src/Chainweb/Pact/SPV.hs b/src/Chainweb/Pact/SPV.hs index 725d0e0c89..a87ff02538 100644 --- a/src/Chainweb/Pact/SPV.hs +++ b/src/Chainweb/Pact/SPV.hs @@ -57,6 +57,7 @@ import Chainweb.BlockHash as CW import Chainweb.BlockHeader import Chainweb.BlockHeaderDB import Chainweb.BlockHeight +import qualified Chainweb.ChainId as CW import Chainweb.Pact.Service.Types import Chainweb.Pact.Utils (aeson) import Chainweb.Payload @@ -66,6 +67,7 @@ import Chainweb.SPV.VerifyProof import Chainweb.TreeDB import Chainweb.Utils import qualified Chainweb.Version as CW +import qualified Chainweb.Version.Guards as CW import Chainweb.Storage.Table @@ -106,7 +108,7 @@ verifySPV verifySPV bdb bh typ proof = go typ proof where cid = CW._chainId bdb - enableBridge = CW.enableSPVBridge (_blockChainwebVersion bh) (_blockHeight bh) + enableBridge = CW.enableSPVBridge (CW._chainwebVersion bh) cid (_blockHeight bh) mkSPVResult' cr j | enableBridge = diff --git a/src/Chainweb/Pact/Service/PactInProcApi.hs b/src/Chainweb/Pact/Service/PactInProcApi.hs index cfd84417f4..926c7f1a0b 100644 --- a/src/Chainweb/Pact/Service/PactInProcApi.hs +++ b/src/Chainweb/Pact/Service/PactInProcApi.hs @@ -46,7 +46,7 @@ import Chainweb.Pact.Service.PactQueue import Chainweb.Payload.PayloadStore import Chainweb.Transaction import Chainweb.Utils -import Chainweb.Version (ChainwebVersion) +import Chainweb.Version import Data.LogMessage diff --git a/src/Chainweb/Pact/Service/Types.hs b/src/Chainweb/Pact/Service/Types.hs index 5f3f62b0d5..68eb18d516 100644 --- a/src/Chainweb/Pact/Service/Types.hs +++ b/src/Chainweb/Pact/Service/Types.hs @@ -46,13 +46,13 @@ import Pact.Types.Persistence import Chainweb.BlockHash import Chainweb.BlockHeader import Chainweb.BlockHeight +import Chainweb.ChainId import Chainweb.Mempool.Mempool (InsertError(..),TransactionHash) import Chainweb.Miner.Pact import Chainweb.Pact.Backend.DbCache import Chainweb.Payload import Chainweb.Transaction import Chainweb.Utils (T2, encodeToText) -import Chainweb.Version -- | Externally-injected PactService properties. -- diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index 9f55737e2e..eb25560854 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -65,7 +65,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Short as SB import Data.Decimal (Decimal, roundTo) import Data.Default (def) -import Data.Foldable (for_, traverse_, foldl') +import Data.Foldable (fold, for_, foldl') import Data.IORef import qualified Data.HashMap.Strict as HM import Data.Maybe @@ -98,15 +98,16 @@ import Pact.Types.SPV import Chainweb.BlockHeader import Chainweb.BlockHeight +import qualified Chainweb.ChainId as Chainweb import Chainweb.Mempool.Mempool (requestKeyToTransactionHash) import Chainweb.Miner.Pact import Chainweb.Pact.Service.Types import Chainweb.Pact.Templates -import Chainweb.Pact.Transactions.UpgradeTransactions import Chainweb.Pact.Types hiding (logError) import Chainweb.Transaction import Chainweb.Utils (encodeToByteString, sshow, tryAllSynchronous, T2(..)) import Chainweb.Version as V +import Chainweb.Version.Guards as V -- -------------------------------------------------------------------------- -- @@ -165,21 +166,13 @@ applyCmd v logger gasLogger pdbenv miner gasModel txCtx spv cmd initialGas mcach if chainweb217Pact' then gasModel else _geGasModel freeGasEnv - executionConfigNoHistory = mkExecutionConfig - $ FlagDisableHistoryInTransactionalMode - : ( [ FlagOldReadOnlyBehavior | isPactBackCompatV16 ] - ++ [ FlagPreserveModuleNameBug | not isModuleNameFix ] - ++ [ FlagPreserveNsModuleInstallBug | not isModuleNameFix2 ] - ++ enablePactEvents' txCtx - ++ enablePact40 txCtx - ++ enablePact420 txCtx - ++ enforceKeysetFormats' txCtx - ++ enablePactModuleMemcheck txCtx - ++ enablePact43 txCtx - ++ enablePact431 txCtx - ++ enablePact44 txCtx - ++ enablePact45 txCtx - ++ enableNewTrans txCtx ) + executionConfigNoHistory = ExecutionConfig + $ S.singleton FlagDisableHistoryInTransactionalMode + <> S.fromList + ([ FlagOldReadOnlyBehavior | isPactBackCompatV16 ] + ++ [ FlagPreserveModuleNameBug | not isModuleNameFix ] + ++ [ FlagPreserveNsModuleInstallBug | not isModuleNameFix2 ]) + <> flagsFor v (ctxChainId txCtx) (ctxCurrentBlockHeight txCtx) cenv = TransactionEnv Transactional pdbenv logger gasLogger (ctxToPublicData txCtx) spv nid gasPrice requestKey (fromIntegral gasLimit) executionConfigNoHistory @@ -189,11 +182,12 @@ applyCmd v logger gasLogger pdbenv miner gasModel txCtx spv cmd initialGas mcach gasLimit = view cmdGasLimit cmd nid = networkIdOf cmd currHeight = ctxCurrentBlockHeight txCtx - isModuleNameFix = enableModuleNameFix v currHeight - isModuleNameFix2 = enableModuleNameFix2 v currHeight - isPactBackCompatV16 = pactBackCompat_v16 v currHeight - chainweb213Pact' = chainweb213Pact (ctxVersion txCtx) (ctxCurrentBlockHeight txCtx) - chainweb217Pact' = chainweb217Pact After (ctxVersion txCtx) (ctxCurrentBlockHeight txCtx) + cid = ctxChainId txCtx + isModuleNameFix = enableModuleNameFix v cid currHeight + isModuleNameFix2 = enableModuleNameFix2 v cid currHeight + isPactBackCompatV16 = pactBackCompat_v16 v cid currHeight + chainweb213Pact' = chainweb213Pact v cid currHeight + chainweb217Pact' = chainweb217Pact v cid currHeight toEmptyPactError (PactError errty _ _ _) = PactError errty def [] mempty toOldListErr pe = pe { peDoc = listErrMsg } @@ -298,6 +292,20 @@ applyGenesisCmd logger dbEnv spv cmd = Left e -> fatal $ "Genesis command failed: " <> sshow e Right r -> r <$ debug "successful genesis tx for request key" +flagsFor :: ChainwebVersion -> V.ChainId -> BlockHeight -> S.Set ExecutionFlag +flagsFor v cid bh = S.fromList $ concat + [ enablePactEvents' v cid bh + , enablePact40 v cid bh + , enablePact420 v cid bh + , enforceKeysetFormats' v cid bh + , enablePactModuleMemcheck v cid bh + , enablePact43 v cid bh + , enablePact431 v cid bh + , enablePact44 v cid bh + , enablePact45 v cid bh + , enableNewTrans v cid bh + ] + applyCoinbase :: ChainwebVersion -> Logger @@ -331,20 +339,14 @@ applyCoinbase v logger dbEnv (Miner mid mks@(MinerKeys mk)) reward@(ParsedDecima let interp = initStateInterpreter initState go interp cexec where - chainweb213Pact' = chainweb213Pact v bh + chainweb213Pact' = chainweb213Pact v cid bh fork1_3InEffect = vuln797Fix v cid bh throwCritical = fork1_3InEffect || enfCBFailure - ec = mkExecutionConfig $ - [ FlagDisableModuleInstall - , FlagDisableHistoryInTransactionalMode ] ++ - enablePactEvents' txCtx ++ - enablePact40 txCtx ++ - enablePact420 txCtx ++ - enablePactModuleMemcheck txCtx ++ - enablePact43 txCtx ++ - enablePact431 txCtx ++ - enablePact44 txCtx ++ - enablePact45 txCtx + ec = ExecutionConfig $ S.delete FlagEnforceKeyFormats $ fold + [ S.singleton FlagDisableModuleInstall + , S.singleton FlagDisableHistoryInTransactionalMode + , flagsFor v (ctxChainId txCtx) (ctxCurrentBlockHeight txCtx) + ] tenv = TransactionEnv Transactional dbEnv logger Nothing (ctxToPublicData txCtx) noSPVSupport Nothing 0.0 rk 0 ec txst = TransactionState mc mempty 0 Nothing (_geGasModel freeGasEnv) @@ -353,7 +355,7 @@ applyCoinbase v logger dbEnv (Miner mid mks@(MinerKeys mk)) reward@(ParsedDecima parent = _tcParentHeader txCtx bh = ctxCurrentBlockHeight txCtx - cid = V._chainId parent + cid = Chainweb._chainId parent chash = Pact.Hash $ SB.toShort $ encodeToByteString $ _blockHash $ _parentHeader parent -- NOTE: it holds that @ _pdPrevBlockHash pd == encode _blockHash@ -- NOTE: chash includes the /quoted/ text of the parent header. @@ -374,7 +376,6 @@ applyCoinbase v logger dbEnv (Miner mid mks@(MinerKeys mk)) reward@(ParsedDecima <> sshow mid upgradedModuleCache <- applyUpgrades v cid bh - void $! applyTwentyChainUpgrade v cid bh -- NOTE (linda): When adding new forking transactions that are injected -- into a block's coinbase transaction, please add a corresponding case @@ -459,12 +460,13 @@ readInitModules logger dbEnv txCtx -- cache purging everything but coin and its -- dependencies. chainweb217Pact' = chainweb217Pact - After (ctxVersion txCtx) + (ctxChainId txCtx) (ctxCurrentBlockHeight txCtx) parent = _tcParentHeader txCtx - v = _chainwebVersion parent + v = ctxVersion txCtx + cid = ctxChainId txCtx h = _blockHeight (_parentHeader parent) + 1 rk = RequestKey chash nid = Nothing @@ -474,7 +476,7 @@ readInitModules logger dbEnv txCtx txst = TransactionState mempty mempty 0 Nothing (_geGasModel freeGasEnv) interp = defaultInterpreter die msg = throwM $ PactInternalError $ "readInitModules: " <> msg - mkCmd = buildExecParsedCode (Just (v, h)) Nothing + mkCmd = buildExecParsedCode (pactParserVersion v cid h) Nothing run msg cmd = do er <- catchesPactError $! applyExec' 0 interp cmd [] chash permissiveNamespacePolicy @@ -537,33 +539,24 @@ readInitModules logger dbEnv txCtx -- applyUpgrades :: ChainwebVersion - -> V.ChainId + -> Chainweb.ChainId -> BlockHeight -> TransactionM p (Maybe ModuleCache) applyUpgrades v cid height - | coinV2Upgrade v cid height = applyCoinV2 - | pact4coin3Upgrade At v height = applyCoinV3 - | chainweb214Pact At v height = applyCoinV4 - | chainweb215Pact At v height = applyCoinV5 - | chainweb217Pact At v height = filterModuleCache + | Just upg <- + v ^? versionUpgrades . onChain cid . at height . _Just = applyUpgrade upg + | cleanModuleCache v cid height = filterModuleCache | otherwise = return Nothing where installCoinModuleAdmin = set (evalCapabilities . capModuleAdmin) $ S.singleton (ModuleName "coin" Nothing) - applyCoinV2 = applyTxs (upgradeTransactions v cid) [FlagDisableInlineMemCheck, FlagDisablePact43, FlagDisablePact45] - - applyCoinV3 = applyTxs coinV3Transactions [FlagDisableInlineMemCheck, FlagDisablePact43, FlagDisablePact45] - - applyCoinV4 = applyTxs coinV4Transactions [FlagDisablePact45] - applyCoinV5 = applyTxs coinV5Transactions [FlagDisablePact45] - filterModuleCache = do mc <- use txCache pure $ Just $ HM.filterWithKey (\k _ -> k == "coin") mc - applyTxs txsIO flags = do + applyUpgrade upg = do infoLog "Applying upgrade!" - txs <- map (fmap payloadObj) <$> liftIO txsIO + let payloads = map (fmap payloadObj) $ _upgradeTransactions upg -- -- In order to prime the module cache with all new modules for subsequent @@ -571,8 +564,11 @@ applyUpgrades v cid height -- those caches is returned. The calling code adds this new cache to the -- init cache in the pact service state (_psInitCache). -- - let execConfig = mkExecutionConfig flags - caches <- local (set txExecutionConfig execConfig) $ mapM applyTx txs + + let flags = flagsFor v cid (if _legacyUpgradeIsPrecocious upg then height + 1 else height) + caches <- local + (txExecutionConfig .~ ExecutionConfig flags) + (mapM applyTx payloads) return $ Just (HM.unions caches) interp = initStateInterpreter @@ -588,40 +584,41 @@ applyUpgrades v cid height logError $ "Upgrade transaction failed! " <> sshow e throwM e -applyTwentyChainUpgrade - :: ChainwebVersion - -> V.ChainId - -> BlockHeight - -> TransactionM p () -applyTwentyChainUpgrade v cid bh - | to20ChainRebalance v cid bh = do - txlist <- liftIO $ twentyChainUpgradeTransactions v cid +-- applyTwentyChainUpgrade +-- :: ChainwebVersion +-- -> Chainweb.ChainId +-- -> BlockHeight +-- -> TransactionM p () +-- applyTwentyChainUpgrade v cid bh +-- | False = do -- atFork To20Chains v cid bh = do +-- let txlist = undefined -- v ^?! versionUpgradeTransactions . to20ChainTransactions . onChain cid - infoLog $ "Applying 20-chain upgrades on chain " <> sshow cid +-- infoLog $ "Applying 20-chain upgrades on chain " <> sshow cid - let txs = fmap payloadObj <$> txlist +-- let txs = undefined -- fmap payloadObj <$> txlist - -- - -- Note (emily): This function does not need to care about - -- module caching, because it is already seeded with the correct cache - -- state, and is not updating the module cache, unlike 'applyUpgrades'. - -- +-- -- +-- -- Note (emily): This function does not need to care about +-- -- module caching, because it is already seeded with the correct cache +-- -- state, and is not updating the module cache, unlike 'applyUpgrades'. +-- -- - traverse_ applyTx txs - | otherwise = return () - where - applyTx tx = do - infoLog $ "Running 20-chain upgrade tx " <> sshow (_cmdHash tx) +-- -- traverse_ applyTx txs +-- undefined +-- | otherwise = return () +-- where +-- applyTx tx = do +-- infoLog $ "Running 20-chain upgrade tx " <> sshow (_cmdHash tx) - let i = initStateInterpreter - $ initCapabilities [mkMagicCapSlot "REMEDIATE"] +-- let i = initStateInterpreter +-- $ initCapabilities [mkMagicCapSlot "REMEDIATE"] - r <- tryAllSynchronous (runGenesis tx permissiveNamespacePolicy i) - case r of - Left e -> do - logError $ "Upgrade transaction failed: " <> sshow e - void $! throwM e - Right _ -> return () +-- r <- tryAllSynchronous (runGenesis tx permissiveNamespacePolicy i) +-- case r of +-- Left e -> do +-- logError $ "Upgrade transaction failed: " <> sshow e +-- void $! throwM e +-- Right _ -> return () @@ -736,56 +733,37 @@ applyExec' initialGas interp (ExecMsg parsedCode execData) senderSigs hsh nsp return er -enablePactEvents' :: TxContext -> [ExecutionFlag] -enablePactEvents' tc - | enablePactEvents (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePactEvents] - -enforceKeysetFormats' :: TxContext -> [ExecutionFlag] -enforceKeysetFormats' tc - | enforceKeysetFormats (ctxVersion tc) (ctxCurrentBlockHeight tc) = [FlagEnforceKeyFormats] - | otherwise = [] - - -enablePact40 :: TxContext -> [ExecutionFlag] -enablePact40 tc - | pact4coin3Upgrade After (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact40] - -enablePact420 :: TxContext -> [ExecutionFlag] -enablePact420 tc - | pact420Upgrade (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact420] - -enablePactModuleMemcheck :: TxContext -> [ExecutionFlag] -enablePactModuleMemcheck tc - | chainweb213Pact (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisableInlineMemCheck] - -enablePact43 :: TxContext -> [ExecutionFlag] -enablePact43 tc - | chainweb214Pact After (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact43] - -enablePact431 :: TxContext -> [ExecutionFlag] -enablePact431 tc - | chainweb215Pact After (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact431] - -enablePact44 :: TxContext -> [ExecutionFlag] -enablePact44 tc - | chainweb216Pact After (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact44] - -enablePact45 :: TxContext -> [ExecutionFlag] -enablePact45 tc - | chainweb217Pact After (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisablePact45] - -enableNewTrans :: TxContext -> [ExecutionFlag] -enableNewTrans tc - | pact44NewTrans (ctxVersion tc) (ctxCurrentBlockHeight tc) = [] - | otherwise = [FlagDisableNewTrans] +-- edtodo use cid +enablePactEvents' :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePactEvents' v cid bh = [FlagDisablePactEvents | not (enablePactEvents v cid bh)] + +-- edtodo use cid +enforceKeysetFormats' :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enforceKeysetFormats' v cid bh = [FlagEnforceKeyFormats | enforceKeysetFormats v cid bh] + +enablePact40 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact40 v cid bh = [FlagDisablePact40 | not (pact4Coin3 v cid bh)] + +enablePact420 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact420 v cid bh = [FlagDisablePact420 | not (pact420 v cid bh)] + +enablePactModuleMemcheck :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePactModuleMemcheck v cid bh = [FlagDisableInlineMemCheck | not (chainweb213Pact v cid bh)] + +enablePact43 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact43 v cid bh = [FlagDisablePact43 | not (chainweb214Pact v cid bh)] + +enablePact431 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact431 v cid bh = [FlagDisablePact431 | not (chainweb215Pact v cid bh)] + +enablePact44 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact44 v cid bh = [FlagDisablePact44 | not (chainweb216Pact v cid bh)] + +enablePact45 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact45 v cid bh = [FlagDisablePact45 | not (chainweb217Pact v cid bh)] + +enableNewTrans :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enableNewTrans v cid bh = [FlagDisableNewTrans | not (pact44NewTrans v cid bh)] -- | Execute a 'ContMsg' and return the command result and module cache -- @@ -807,7 +785,7 @@ applyContinuation initialGas interp cm senderSigs hsh nsp = do _erGas (Just logs) _erExec Nothing) _erEvents -setEnvGas :: Gas -> EvalEnv e -> TransactionM p () +setEnvGas :: Gas -> EvalEnv e -> TransactionM p () setEnvGas initialGas = liftIO . views eeGas (`writeIORef` initialGas) -- | Execute a 'ContMsg' and return just eval result, not wrapped in a @@ -1124,13 +1102,13 @@ mkMagicCapSlot c = CapSlot CapCallStack cap [] -- the 'ExecMsg'. -- buildExecParsedCode - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> Maybe Value -> Text -> IO (ExecMsg ParsedCode) -buildExecParsedCode chainCtx value code = maybe (go Null) go value +buildExecParsedCode ppv value code = maybe (go Null) go value where - go val = case parsePact chainCtx code of + go val = case parsePact ppv code of Right !t -> pure $! ExecMsg t val -- if we can't construct coin contract calls, this should -- fail fast diff --git a/src/Chainweb/Pact/Transactions/CoinV3Transactions.hs b/src/Chainweb/Pact/Transactions/CoinV3Transactions.hs index 9a6187641d..f027f015f3 100644 --- a/src/Chainweb/Pact/Transactions/CoinV3Transactions.hs +++ b/src/Chainweb/Pact/Transactions/CoinV3Transactions.hs @@ -5,14 +5,15 @@ module Chainweb.Pact.Transactions.CoinV3Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiRkd0RlNjcW1neklEQzlENkUwSUtQSFN0ZDhPdW9JdVhRanp4TFdyWTBZayIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIChibGVzcyBcXFwidXRfSl9aTmtveWFQVUVKaGl3VmVXbmtTUW45SlQ5c1FDV0tkampWVnJXb1xcXCIpXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IFNjaGVtYXMgYW5kIFRhYmxlc1xcblxcbiAgKGRlZnNjaGVtYSBjb2luLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJUaGUgY29pbiBjb250cmFjdCB0b2tlbiBzY2hlbWFcXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKD49IGJhbGFuY2UgMC4wKSkgXVxcblxcbiAgICBiYWxhbmNlOmRlY2ltYWxcXG4gICAgZ3VhcmQ6Z3VhcmQpXFxuXFxuICAoZGVmdGFibGUgY29pbi10YWJsZTp7Y29pbi1zY2hlbWF9KVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDYXBhYmlsaXRpZXNcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZSBmYWxzZSBcXFwiRW5mb3JjZSBub24tdXBncmFkZWFiaWxpdHlcXFwiKSlcXG5cXG4gIChkZWZjYXAgR0FTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgZ2FzIGJ1eSBhbmQgcmVkZWVtXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBDT0lOQkFTRSAoKVxcbiAgICBcXFwiTWFnaWMgY2FwYWJpbGl0eSB0byBwcm90ZWN0IG1pbmVyIHJld2FyZFxcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgR0VORVNJUyAoKVxcbiAgICBcXFwiTWFnaWMgY2FwYWJpbGl0eSBjb25zdHJhaW5pbmcgZ2VuZXNpcyB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIFJFTUVESUFURSAoKVxcbiAgICBcXFwiTWFnaWMgY2FwYWJpbGl0eSBmb3IgcmVtZWRpYXRpb24gdHJhbnNhY3Rpb25zXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBERUJJVCAoc2VuZGVyOnN0cmluZylcXG4gICAgXFxcIkNhcGFiaWxpdHkgZm9yIG1hbmFnaW5nIGRlYml0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlLWd1YXJkIChhdCAnZ3VhcmQgKHJlYWQgY29pbi10YWJsZSBzZW5kZXIpKSlcXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciBcXFwiXFxcIikgXFxcInZhbGlkIHNlbmRlclxcXCIpKVxcblxcbiAgKGRlZmNhcCBDUkVESVQgKHJlY2VpdmVyOnN0cmluZylcXG4gICAgXFxcIkNhcGFiaWxpdHkgZm9yIG1hbmFnaW5nIGNyZWRpdGluZyBvcGVyYXRpb25zXFxcIlxcbiAgICAoZW5mb3JjZSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpIFxcXCJ2YWxpZCByZWNlaXZlclxcXCIpKVxcblxcbiAgKGRlZmNhcCBST1RBVEUgKGFjY291bnQ6c3RyaW5nKVxcbiAgICBAZG9jIFxcXCJBdXRvbm9tb3VzbHkgbWFuYWdlZCBjYXBhYmlsaXR5IGZvciBndWFyZCByb3RhdGlvblxcXCJcXG4gICAgQG1hbmFnZWRcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBtYW5hZ2VkIGFtb3VudCBUUkFOU0ZFUi1tZ3JcXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciByZWNlaXZlcikgXFxcInNhbWUgc2VuZGVyIGFuZCByZWNlaXZlclxcXCIpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiUG9zaXRpdmUgYW1vdW50XFxcIilcXG4gICAgKGNvbXBvc2UtY2FwYWJpbGl0eSAoREVCSVQgc2VuZGVyKSlcXG4gICAgKGNvbXBvc2UtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKSlcXG4gIClcXG5cXG4gIChkZWZ1biBUUkFOU0ZFUi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgKGxldCAoKG5ld2JhbCAoLSBtYW5hZ2VkIHJlcXVlc3RlZCkpKVxcbiAgICAgIChlbmZvcmNlICg-PSBuZXdiYWwgMC4wKVxcbiAgICAgICAgKGZvcm1hdCBcXFwiVFJBTlNGRVIgZXhjZWVkZWQgZm9yIGJhbGFuY2Uge31cXFwiIFttYW5hZ2VkXSkpXFxuICAgICAgbmV3YmFsKVxcbiAgKVxcblxcbiAgOyB2MyBjYXBhYmlsaXRpZXNcXG4gIChkZWZjYXAgUkVMRUFTRV9BTExPQ0FUSU9OXFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIkV2ZW50IGZvciBhbGxvY2F0aW9uIHJlbGVhc2UsIGNhbiBiZSB1c2VkIGZvciBzaWcgc2NvcGluZy5cXFwiXFxuICAgIEBldmVudCB0cnVlXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgc2VuZGVyIG1pbmVyIGZlZSkpIDt2M1xcblxcbiAgICAgICAgOyBkaXJlY3RseSB1cGRhdGUgaW5zdGVhZCBvZiBjcmVkaXRcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgc2VuZGVyKVxcbiAgICAgICAgKGlmICg-IHJlZnVuZCAwLjApXFxuICAgICAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBzZW5kZXJcXG4gICAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG4gICAgICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIjogKCsgYmFsYW5jZSByZWZ1bmQpIH0pKVxcblxcbiAgICAgICAgICBcXFwibm9vcFxcXCIpKVxcblxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBtaW5lcilcXG4gICAgICAgIChpZiAoPiBmZWUgMC4wKVxcbiAgICAgICAgICAoY3JlZGl0IG1pbmVyIG1pbmVyLWd1YXJkIGZlZSlcXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG4gICAgICApXFxuXFxuICAgIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWNjb3VudDpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGd1YXJkOmd1YXJkKVxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UtcmVzZXJ2ZWQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgKGluc2VydCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IDAuMFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiAgIDogZ3VhcmRcXG4gICAgICB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gZ2V0LWJhbGFuY2U6ZGVjaW1hbCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgYmFsYW5jZVxcbiAgICAgIClcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRldGFpbHM6b2JqZWN0e2Z1bmdpYmxlLXYyLmFjY291bnQtZGV0YWlsc31cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcbiAgICAgIHsgXFxcImFjY291bnRcXFwiIDogYWNjb3VudFxcbiAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogYmFsXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiOiBnIH0pXFxuICAgIClcXG5cXG4gIChkZWZ1biByb3RhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBuZXctZ3VhcmQ6Z3VhcmQpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFJPVEFURSBhY2NvdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJndWFyZFxcXCIgOj0gb2xkLWd1YXJkIH1cXG5cXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIG9sZC1ndWFyZClcXG5cXG4gICAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgIHsgXFxcImd1YXJkXFxcIiA6IG5ldy1ndWFyZCB9XFxuICAgICAgICAgICkpKVxcbiAgICApXFxuXFxuXFxuICAoZGVmdW4gcHJlY2lzaW9uOmludGVnZXJcXG4gICAgKClcXG4gICAgTUlOSU1VTV9QUkVDSVNJT04pXFxuXFxuICAoZGVmdW4gdHJhbnNmZXI6c3RyaW5nIChzZW5kZXI6c3RyaW5nIHJlY2VpdmVyOnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBzZW5kZXIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgcmVjZWl2ZXIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpIF1cXG5cXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciByZWNlaXZlcilcXG4gICAgICBcXFwic2VuZGVyIGNhbm5vdCBiZSB0aGUgcmVjZWl2ZXIgb2YgYSB0cmFuc2ZlclxcXCIpXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcInRyYW5zZmVyIGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFRSQU5TRkVSIHNlbmRlciByZWNlaXZlciBhbW91bnQpXFxuICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIHJlY2VpdmVyXFxuICAgICAgICB7IFxcXCJndWFyZFxcXCIgOj0gZyB9XFxuXFxuICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIGcgYW1vdW50KSlcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biB0cmFuc2Zlci1jcmVhdGU6c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgYW1vdW50OmRlY2ltYWwgKVxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgY29uc2VydmVzLW1hc3MpIF1cXG5cXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciByZWNlaXZlcilcXG4gICAgICBcXFwic2VuZGVyIGNhbm5vdCBiZSB0aGUgcmVjZWl2ZXIgb2YgYSB0cmFuc2ZlclxcXCIpXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcInRyYW5zZmVyIGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFRSQU5TRkVSIHNlbmRlciByZWNlaXZlciBhbW91bnQpXFxuICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuICAgICAgKGNyZWRpdCByZWNlaXZlciByZWNlaXZlci1ndWFyZCBhbW91bnQpKVxcbiAgICApXFxuXFxuICAoZGVmdW4gY29pbmJhc2U6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhY2NvdW50LWd1YXJkOmd1YXJkIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJJbnRlcm5hbCBmdW5jdGlvbiBmb3IgdGhlIGluaXRpYWwgY3JlYXRpb24gb2YgY29pbnMuICBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgXFxcXGNhbm5vdCBiZSB1c2VkIG91dHNpZGUgb2YgdGhlIGNvaW4gY29udHJhY3QuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENPSU5CQVNFKSlcXG4gICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIGFjY291bnQgYW1vdW50KSkgO3YzXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIGFjY291bnQgYW1vdW50KSkgO3YzXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuXFxuICAgICAgKGVuZm9yY2UgKDw9IGFtb3VudCBiYWxhbmNlKSBcXFwiSW5zdWZmaWNpZW50IGZ1bmRzXFxcIilcXG5cXG4gICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAoLSBiYWxhbmNlIGFtb3VudCkgfVxcbiAgICAgICAgKSlcXG4gICAgKVxcblxcbiAgKGRlZnBhY3QgZnVuZC10eCAoc2VuZGVyOnN0cmluZyBtaW5lcjpzdHJpbmcgbWluZXItZ3VhcmQ6Z3VhcmQgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiJ2Z1bmQtdHgnIGlzIGEgc3BlY2lhbCBwYWN0IHRvIGZ1bmQgYSB0cmFuc2FjdGlvbiBpbiB0d28gc3RlcHMsICAgICBcXFxcXFxuICAgIFxcXFx3aXRoIHRoZSBhY3R1YWwgdHJhbnNhY3Rpb24gdHJhbnNwaXJpbmcgaW4gdGhlIG1pZGRsZTogICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxcXFxcXG4gICAgXFxcXCAgMSkgQSBidXlpbmcgcGhhc2UsIGRlYml0aW5nIHRoZSBzZW5kZXIgZm9yIHRvdGFsIGdhcyBhbmQgZmVlLCB5aWVsZGluZyBcXFxcXFxuICAgIFxcXFwgICAgIFRYX01BWF9DSEFSR0UuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAyKSBBIHNldHRsZW1lbnQgcGhhc2UsIHJlc3VtaW5nIFRYX01BWF9DSEFSR0UsIGFuZCBhbGxvY2F0aW5nIHRvIHRoZSAgIFxcXFxcXG4gICAgXFxcXCAgICAgY29pbmJhc2UgYWNjb3VudCBmb3IgdXNlZCBnYXMgYW5kIGZlZSwgYW5kIHNlbmRlciBhY2NvdW50IGZvciBiYWwtICBcXFxcXFxuICAgIFxcXFwgICAgIGFuY2UgKHVudXNlZCBnYXMsIGlmIGFueSkuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICAgIDsocHJvcGVydHkgY29uc2VydmVzLW1hc3MpIG5vdCBzdXBwb3J0ZWQgeWV0XFxuICAgICAgICAgICBdXFxuXFxuICAgIChzdGVwIChidXktZ2FzIHNlbmRlciB0b3RhbCkpXFxuICAgIChzdGVwIChyZWRlZW0tZ2FzIG1pbmVyIG1pbmVyLWd1YXJkIHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZWJpdDpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJEZWJpdCBBTU9VTlQgZnJvbSBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJkZWJpdCBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChERUJJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICAoZGVmdW4gY3JlZGl0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgZ3VhcmQ6Z3VhcmQgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkNyZWRpdCBBTU9VTlQgdG8gQUNDT1VOVCBiYWxhbmNlXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMCkgXFxcImNyZWRpdCBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDUkVESVQgYWNjb3VudCkpXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IC0xLjAsIFxcXCJndWFyZFxcXCIgOiBndWFyZCB9XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSwgXFxcImd1YXJkXFxcIiA6PSByZXRnIH1cXG4gICAgICA7IHdlIGRvbid0IHdhbnQgdG8gb3ZlcndyaXRlIGFuIGV4aXN0aW5nIGd1YXJkIHdpdGggdGhlIHVzZXItc3VwcGxpZWQgb25lXFxuICAgICAgKGVuZm9yY2UgKD0gcmV0ZyBndWFyZClcXG4gICAgICAgIFxcXCJhY2NvdW50IGd1YXJkcyBkbyBub3QgbWF0Y2hcXFwiKVxcblxcbiAgICAgIChsZXQgKChpcy1uZXdcXG4gICAgICAgICAgICAgKGlmICg9IGJhbGFuY2UgLTEuMClcXG4gICAgICAgICAgICAgICAgIChlbmZvcmNlLXJlc2VydmVkIGFjY291bnQgZ3VhcmQpXFxuICAgICAgICAgICAgICAgZmFsc2UpKSlcXG5cXG4gICAgICAgICh3cml0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAoaWYgaXMtbmV3IGFtb3VudCAoKyBiYWxhbmNlIGFtb3VudCkpXFxuICAgICAgICAgICwgXFxcImd1YXJkXFxcIiAgIDogcmV0Z1xcbiAgICAgICAgICB9KSlcXG4gICAgICApKVxcblxcbiAgKGRlZnVuIGNoZWNrLXJlc2VydmVkOnN0cmluZyAoYWNjb3VudDpzdHJpbmcpXFxuICAgIFxcXCIgQ2hlY2tzIEFDQ09VTlQgZm9yIHJlc2VydmVkIG5hbWUgYW5kIHJldHVybnMgdHlwZSBpZiBcXFxcXFxuICAgIFxcXFwgZm91bmQgb3IgZW1wdHkgc3RyaW5nLiBSZXNlcnZlZCBuYW1lcyBzdGFydCB3aXRoIGEgXFxcXFxcbiAgICBcXFxcIHNpbmdsZSBjaGFyIGFuZCBjb2xvbiwgZS5nLiAnYzpmb28nLCB3aGljaCB3b3VsZCByZXR1cm4gJ2MnIGFzIHR5cGUuXFxcIlxcbiAgICAobGV0ICgocGZ4ICh0YWtlIDIgYWNjb3VudCkpKVxcbiAgICAgIChpZiAoPSBcXFwiOlxcXCIgKHRha2UgLTEgcGZ4KSkgKHRha2UgMSBwZngpIFxcXCJcXFwiKSkpXFxuXFxuICAoZGVmdW4gZW5mb3JjZS1yZXNlcnZlZDpib29sIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSByZXNlcnZlZCBhY2NvdW50IG5hbWUgcHJvdG9jb2xzLlxcXCJcXG4gICAgKGxldCAoKHIgKGNoZWNrLXJlc2VydmVkIGFjY291bnQpKSlcXG4gICAgICAoaWYgKD0gXFxcIlxcXCIgcikgdHJ1ZVxcbiAgICAgICAgKGlmICg9IFxcXCJrXFxcIiByKVxcbiAgICAgICAgICAoZW5mb3JjZVxcbiAgICAgICAgICAgICg9IChmb3JtYXQgXFxcInt9XFxcIiBbZ3VhcmRdKVxcbiAgICAgICAgICAgICAgIChmb3JtYXQgXFxcIktleVNldCB7a2V5czogW3t9XSxwcmVkOiBrZXlzLWFsbH1cXFwiXFxuICAgICAgICAgICAgICAgICAgICAgICBbKGRyb3AgMiBhY2NvdW50KV0pKVxcbiAgICAgICAgICAgIFxcXCJTaW5nbGUta2V5IGFjY291bnQgcHJvdG9jb2wgdmlvbGF0aW9uXFxcIilcXG4gICAgICAgICAgKGVuZm9yY2UgZmFsc2VcXG4gICAgICAgICAgICAoZm9ybWF0IFxcXCJVbnJlY29nbml6ZWQgcmVzZXJ2ZWQgcHJvdG9jb2w6IHt9XFxcIiBbcl0pKSkpKSlcXG5cXG5cXG4gIChkZWZzY2hlbWEgY3Jvc3NjaGFpbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiU2NoZW1hIGZvciB5aWVsZGVkIHZhbHVlIGluIGNyb3NzLWNoYWluIHRyYW5zZmVyc1xcXCJcXG4gICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgIGFtb3VudDpkZWNpbWFsKVxcblxcbiAgKGRlZnBhY3QgdHJhbnNmZXItY3Jvc3NjaGFpbjpzdHJpbmdcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICB0YXJnZXQtY2hhaW46c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWwgKVxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBzZW5kZXIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgcmVjZWl2ZXIpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAoc3RlcFxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcilcXG5cXG4gICAgICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKCE9IFxcXCJcXFwiIHRhcmdldC1jaGFpbikgXFxcImVtcHR5IHRhcmdldC1jaGFpblxcXCIpXFxuICAgICAgICAoZW5mb3JjZSAoIT0gKGF0ICdjaGFpbi1pZCAoY2hhaW4tZGF0YSkpIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgXFxcImNhbm5vdCBydW4gY3Jvc3MtY2hhaW4gdHJhbnNmZXJzIHRvIHRoZSBzYW1lIGNoYWluXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgICAgIFxcXCJ0cmFuc2ZlciBxdWFudGl0eSBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAgICAgOzsgc3RlcCAxIC0gZGViaXQgZGVsZXRlLWFjY291bnQgb24gY3VycmVudCBjaGFpblxcbiAgICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuXFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgc2VuZGVyIFxcXCJcXFwiIGFtb3VudCkpXFxuXFxuICAgICAgICAobGV0XFxuICAgICAgICAgICgoY3Jvc3NjaGFpbi1kZXRhaWxzOm9iamVjdHtjcm9zc2NoYWluLXNjaGVtYX1cXG4gICAgICAgICAgICB7IFxcXCJyZWNlaXZlclxcXCIgOiByZWNlaXZlclxcbiAgICAgICAgICAgICwgXFxcInJlY2VpdmVyLWd1YXJkXFxcIiA6IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAgICAgLCBcXFwiYW1vdW50XFxcIiA6IGFtb3VudFxcbiAgICAgICAgICAgIH0pKVxcbiAgICAgICAgICAoeWllbGQgY3Jvc3NjaGFpbi1kZXRhaWxzIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgKSkpXFxuXFxuICAgIChzdGVwXFxuICAgICAgKHJlc3VtZVxcbiAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDo9IHJlY2VpdmVyXFxuICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOj0gcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICwgXFxcImFtb3VudFxcXCIgOj0gYW1vdW50XFxuICAgICAgICB9XFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgcmVjZWl2ZXIgYW1vdW50KSlcXG4gICAgICAgIDs7IHN0ZXAgMiAtIGNyZWRpdCBjcmVhdGUgYWNjb3VudCBvbiB0YXJnZXQgY2hhaW5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCByZWNlaXZlcilcXG4gICAgICAgICAgKGNyZWRpdCByZWNlaXZlciByZWNlaXZlci1ndWFyZCBhbW91bnQpKVxcbiAgICAgICAgKSlcXG4gICAgKVxcblxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIGFsbG9jYXRpb25zXFxuXFxuICAoZGVmc2NoZW1hIGFsbG9jYXRpb24tc2NoZW1hXFxuICAgIEBkb2MgXFxcIkdlbmVzaXMgYWxsb2NhdGlvbiByZWdpc3RyeVxcXCJcXG4gICAgO0Btb2RlbCBbIChpbnZhcmlhbnQgKD49IGJhbGFuY2UgMC4wKSkgXVxcblxcbiAgICBiYWxhbmNlOmRlY2ltYWxcXG4gICAgZGF0ZTp0aW1lXFxuICAgIGd1YXJkOmd1YXJkXFxuICAgIHJlZGVlbWVkOmJvb2wpXFxuXFxuICAoZGVmdGFibGUgYWxsb2NhdGlvbi10YWJsZTp7YWxsb2NhdGlvbi1zY2hlbWF9KVxcblxcbiAgKGRlZnVuIGNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnRcXG4gICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgIGRhdGU6dGltZVxcbiAgICAgIGtleXNldC1yZWY6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICBAZG9jIFxcXCJBZGQgYW4gZW50cnkgdG8gdGhlIGNvaW4gYWxsb2NhdGlvbiB0YWJsZS4gVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgICAgICAgXFxcXGFsc28gY3JlYXRlcyBhIGNvcnJlc3BvbmRpbmcgZW1wdHkgY29pbiBjb250cmFjdCBhY2NvdW50IFxcXFxcXG4gICAgICAgICBcXFxcb2YgdGhlIHNhbWUgbmFtZSBhbmQgZ3VhcmQuIFJlcXVpcmVzIEdFTkVTSVMgY2FwYWJpbGl0eS4gXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpIF1cXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoR0VORVNJUykpXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlICg-PSBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJhbGxvY2F0aW9uIGFtb3VudCBtdXN0IGJlIG5vbi1uZWdhdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAobGV0XFxuICAgICAgKChndWFyZDpndWFyZCAoa2V5c2V0LXJlZi1ndWFyZCBrZXlzZXQtcmVmKSkpXFxuXFxuICAgICAgKGNyZWF0ZS1hY2NvdW50IGFjY291bnQgZ3VhcmQpXFxuXFxuICAgICAgKGluc2VydCBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogYW1vdW50XFxuICAgICAgICAsIFxcXCJkYXRlXFxcIiA6IGRhdGVcXG4gICAgICAgICwgXFxcImd1YXJkXFxcIiA6IGd1YXJkXFxuICAgICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOiBmYWxzZVxcbiAgICAgICAgfSkpKVxcblxcbiAgKGRlZnVuIHJlbGVhc2UtYWxsb2NhdGlvblxcbiAgICAoIGFjY291bnQ6c3RyaW5nIClcXG5cXG4gICAgQGRvYyBcXFwiUmVsZWFzZSBmdW5kcyBhc3NvY2lhdGVkIHdpdGggYWxsb2NhdGlvbiBBQ0NPVU5UIGludG8gbWFpbiBsZWRnZXIuICAgXFxcXFxcbiAgICAgICAgIFxcXFxBQ0NPVU5UIG11c3QgYWxyZWFkeSBleGlzdCBpbiBtYWluIGxlZGdlci4gQWxsb2NhdGlvbiBpcyBkZWFjdGl2YXRlZCBcXFxcXFxuICAgICAgICAgXFxcXGFmdGVyIHJlbGVhc2UuXFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKHdpdGgtcmVhZCBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlXFxuICAgICAgLCBcXFwiZGF0ZVxcXCIgOj0gcmVsZWFzZS10aW1lXFxuICAgICAgLCBcXFwicmVkZWVtZWRcXFwiIDo9IHJlZGVlbWVkXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiIDo9IGd1YXJkXFxuICAgICAgfVxcblxcbiAgICAgIChsZXQgKChjdXJyLXRpbWU6dGltZSAoYXQgJ2Jsb2NrLXRpbWUgKGNoYWluLWRhdGEpKSkpXFxuXFxuICAgICAgICAoZW5mb3JjZSAobm90IHJlZGVlbWVkKVxcbiAgICAgICAgICBcXFwiYWxsb2NhdGlvbiBmdW5kcyBoYXZlIGFscmVhZHkgYmVlbiByZWRlZW1lZFxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZVxcbiAgICAgICAgICAoPj0gY3Vyci10aW1lIHJlbGVhc2UtdGltZSlcXG4gICAgICAgICAgKGZvcm1hdCBcXFwiZnVuZHMgbG9ja2VkIHVudGlsIHt9LiBjdXJyZW50IHRpbWU6IHt9XFxcIiBbcmVsZWFzZS10aW1lIGN1cnItdGltZV0pKVxcblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoUkVMRUFTRV9BTExPQ0FUSU9OIGFjY291bnQgYmFsYW5jZSlcXG5cXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIGd1YXJkKVxcblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIGFjY291bnQpXFxuICAgICAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBcXFwiXFxcIiBhY2NvdW50IGJhbGFuY2UpKVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKSlcXG4gICAgKSkpXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiY29pbi1jb250cmFjdC12M1wifSJ9" ] diff --git a/src/Chainweb/Pact/Transactions/CoinV4Transactions.hs b/src/Chainweb/Pact/Transactions/CoinV4Transactions.hs index 2358ec73f6..d2bc73f404 100644 --- a/src/Chainweb/Pact/Transactions/CoinV4Transactions.hs +++ b/src/Chainweb/Pact/Transactions/CoinV4Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.CoinV4Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoieW5ucDFYVVNSTjJrMUYwYTZ2dXM3RFp0SDZjcHN6MVhmX0d3V0xnTFhTTSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUteGNoYWluLXYxXFxuXFxuICBcXFwiIFRoaXMgaW50ZXJmYWNlIG9mZmVycyBhIHN0YW5kYXJkIGNhcGFiaWxpdHkgZm9yIGNyb3NzLWNoYWluIFxcXFxcXG4gIFxcXFwgdHJhbnNmZXJzIGFuZCBhc3NvY2lhdGVkIGV2ZW50cy4gXFxcIlxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU46Ym9vbFxcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgKVxcbiAgICBAZG9jIFxcXCIgTWFuYWdlZCBjYXBhYmlsaXR5IHNlYWxpbmcgQU1PVU5UIGZvciB0cmFuc2ZlciBcXFxcXFxuICAgICAgICAgXFxcXCBmcm9tIFNFTkRFUiB0byBSRUNFSVZFUiBvbiBUQVJHRVQtQ0hBSU4uIFBlcm1pdHMgXFxcXFxcbiAgICAgICAgIFxcXFwgYW55IG51bWJlciBvZiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdXAgdG8gQU1PVU5ULlxcXCJcXG5cXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSX1hDSEFJTi1tZ3JcXG4gICAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG4gICAgQGRvYyBcXFwiIEFsbG93cyBUUkFOU0ZFUi1YQ0hBSU4gQU1PVU5UIHRvIGJlIGxlc3MgdGhhbiBvciBcXFxcXFxuICAgICAgICAgXFxcXCBlcXVhbCBtYW5hZ2VkIHF1YW50aXR5IGFzIGEgb25lLXNob3QsIHJldHVybmluZyAwLjAuXFxcIlxcbiAgKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU5fUkVDRDpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgc291cmNlLWNoYWluOnN0cmluZ1xcbiAgICApXFxuICAgIEBkb2MgXFxcIkV2ZW50IGVtaXR0ZWQgb24gcmVjZWlwdCBvZiBjcm9zcy1jaGFpbiB0cmFuc2Zlci5cXFwiXFxuICAgIEBldmVudFxcbiAgKVxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImdlbmVzaXMteGNoYWluXCJ9In0" , "eyJoYXNoIjoiby1RNlV2RU4tSmNXSFozUnpvM0lET3R5czRkUTFKX2pwN25vUXdoWEMwVSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG4gIChpbXBsZW1lbnRzIGZ1bmdpYmxlLXhjaGFpbi12MSlcXG5cXG4gIDs7IGNvaW4tdjJcXG4gIChibGVzcyBcXFwidXRfSl9aTmtveWFQVUVKaGl3VmVXbmtTUW45SlQ5c1FDV0tkampWVnJXb1xcXCIpXFxuXFxuICA7OyBjb2luIHYzXFxuICAoYmxlc3MgXFxcIjFvc19zTEFVWXZCenNwbjVqamF3dFJwSldpSDFXUGZoeU5yYWVWdlNJd1VcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBTY2hlbWFzIGFuZCBUYWJsZXNcXG5cXG4gIChkZWZzY2hlbWEgY29pbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiVGhlIGNvaW4gY29udHJhY3QgdG9rZW4gc2NoZW1hXFxcIlxcbiAgICBAbW9kZWwgWyAoaW52YXJpYW50ICg-PSBiYWxhbmNlIDAuMCkpIF1cXG5cXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcbiAgKGRlZnRhYmxlIGNvaW4tdGFibGU6e2NvaW4tc2NoZW1hfSlcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgQ2FwYWJpbGl0aWVzXFxuXFxuICAoZGVmY2FwIEdPVkVSTkFOQ0UgKClcXG4gICAgKGVuZm9yY2UgZmFsc2UgXFxcIkVuZm9yY2Ugbm9uLXVwZ3JhZGVhYmlsaXR5XFxcIikpXFxuXFxuICAoZGVmY2FwIEdBUyAoKVxcbiAgICBcXFwiTWFnaWMgY2FwYWJpbGl0eSB0byBwcm90ZWN0IGdhcyBidXkgYW5kIHJlZGVlbVxcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgQ09JTkJBU0UgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBtaW5lciByZXdhcmRcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIEdFTkVTSVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgY29uc3RyYWluaW5nIGdlbmVzaXMgdHJhbnNhY3Rpb25zXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBSRU1FRElBVEUgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgZm9yIHJlbWVkaWF0aW9uIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgREVCSVQgKHNlbmRlcjpzdHJpbmcpXFxuICAgIFxcXCJDYXBhYmlsaXR5IGZvciBtYW5hZ2luZyBkZWJpdGluZyBvcGVyYXRpb25zXFxcIlxcbiAgICAoZW5mb3JjZS1ndWFyZCAoYXQgJ2d1YXJkIChyZWFkIGNvaW4tdGFibGUgc2VuZGVyKSkpXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgXFxcIlxcXCIpIFxcXCJ2YWxpZCBzZW5kZXJcXFwiKSlcXG5cXG4gIChkZWZjYXAgQ1JFRElUIChyZWNlaXZlcjpzdHJpbmcpXFxuICAgIFxcXCJDYXBhYmlsaXR5IGZvciBtYW5hZ2luZyBjcmVkaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UgKCE9IHJlY2VpdmVyIFxcXCJcXFwiKSBcXFwidmFsaWQgcmVjZWl2ZXJcXFwiKSlcXG5cXG4gIChkZWZjYXAgUk9UQVRFIChhY2NvdW50OnN0cmluZylcXG4gICAgQGRvYyBcXFwiQXV0b25vbW91c2x5IG1hbmFnZWQgY2FwYWJpbGl0eSBmb3IgZ3VhcmQgcm90YXRpb25cXFwiXFxuICAgIEBtYW5hZ2VkXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSOmJvb2xcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgKVxcbiAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpIFxcXCJzYW1lIHNlbmRlciBhbmQgcmVjZWl2ZXJcXFwiKVxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMCkgXFxcIlBvc2l0aXZlIGFtb3VudFxcXCIpXFxuICAgIChjb21wb3NlLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcikpXFxuICAgIChjb21wb3NlLWNhcGFiaWxpdHkgKENSRURJVCByZWNlaXZlcikpXFxuICApXFxuXFxuICAoZGVmdW4gVFJBTlNGRVItbWdyOmRlY2ltYWxcXG4gICAgKCBtYW5hZ2VkOmRlY2ltYWxcXG4gICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIChsZXQgKChuZXdiYWwgKC0gbWFuYWdlZCByZXF1ZXN0ZWQpKSlcXG4gICAgICAoZW5mb3JjZSAoPj0gbmV3YmFsIDAuMClcXG4gICAgICAgIChmb3JtYXQgXFxcIlRSQU5TRkVSIGV4Y2VlZGVkIGZvciBiYWxhbmNlIHt9XFxcIiBbbWFuYWdlZF0pKVxcbiAgICAgIG5ld2JhbClcXG4gIClcXG5cXG4gIChkZWZjYXAgVFJBTlNGRVJfWENIQUlOOmJvb2xcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgICB0YXJnZXQtY2hhaW46c3RyaW5nXFxuICAgIClcXG5cXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSX1hDSEFJTi1tZ3JcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJDcm9zcy1jaGFpbiB0cmFuc2ZlcnMgcmVxdWlyZSBhIHBvc2l0aXZlIGFtb3VudFxcXCIpXFxuICAgIChjb21wb3NlLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcikpXFxuICApXFxuXFxuICAoZGVmdW4gVFJBTlNGRVJfWENIQUlOLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAoZW5mb3JjZSAoPj0gbWFuYWdlZCByZXF1ZXN0ZWQpXFxuICAgICAgKGZvcm1hdCBcXFwiVFJBTlNGRVJfWENIQUlOIGV4Y2VlZGVkIGZvciBiYWxhbmNlIHt9XFxcIiBbbWFuYWdlZF0pKVxcbiAgICAwLjBcXG4gIClcXG5cXG4gIChkZWZjYXAgVFJBTlNGRVJfWENIQUlOX1JFQ0Q6Ym9vbFxcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgIHNvdXJjZS1jaGFpbjpzdHJpbmdcXG4gICAgKVxcbiAgICBAZXZlbnQgdHJ1ZVxcbiAgKVxcblxcbiAgOyB2MyBjYXBhYmlsaXRpZXNcXG4gIChkZWZjYXAgUkVMRUFTRV9BTExPQ0FUSU9OXFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIkV2ZW50IGZvciBhbGxvY2F0aW9uIHJlbGVhc2UsIGNhbiBiZSB1c2VkIGZvciBzaWcgc2NvcGluZy5cXFwiXFxuICAgIEBldmVudCB0cnVlXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgc2VuZGVyIG1pbmVyIGZlZSkpIDt2M1xcblxcbiAgICAgICAgOyBkaXJlY3RseSB1cGRhdGUgaW5zdGVhZCBvZiBjcmVkaXRcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgc2VuZGVyKVxcbiAgICAgICAgKGlmICg-IHJlZnVuZCAwLjApXFxuICAgICAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBzZW5kZXJcXG4gICAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG4gICAgICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIjogKCsgYmFsYW5jZSByZWZ1bmQpIH0pKVxcblxcbiAgICAgICAgICBcXFwibm9vcFxcXCIpKVxcblxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBtaW5lcilcXG4gICAgICAgIChpZiAoPiBmZWUgMC4wKVxcbiAgICAgICAgICAoY3JlZGl0IG1pbmVyIG1pbmVyLWd1YXJkIGZlZSlcXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG4gICAgICApXFxuXFxuICAgIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWNjb3VudDpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGd1YXJkOmd1YXJkKVxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UtcmVzZXJ2ZWQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgKGluc2VydCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IDAuMFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiAgIDogZ3VhcmRcXG4gICAgICB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gZ2V0LWJhbGFuY2U6ZGVjaW1hbCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgYmFsYW5jZVxcbiAgICAgIClcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRldGFpbHM6b2JqZWN0e2Z1bmdpYmxlLXYyLmFjY291bnQtZGV0YWlsc31cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcbiAgICAgIHsgXFxcImFjY291bnRcXFwiIDogYWNjb3VudFxcbiAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogYmFsXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiOiBnIH0pXFxuICAgIClcXG5cXG4gIChkZWZ1biByb3RhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBuZXctZ3VhcmQ6Z3VhcmQpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFJPVEFURSBhY2NvdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJndWFyZFxcXCIgOj0gb2xkLWd1YXJkIH1cXG5cXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIG9sZC1ndWFyZClcXG5cXG4gICAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgIHsgXFxcImd1YXJkXFxcIiA6IG5ldy1ndWFyZCB9XFxuICAgICAgICAgICkpKVxcbiAgICApXFxuXFxuXFxuICAoZGVmdW4gcHJlY2lzaW9uOmludGVnZXJcXG4gICAgKClcXG4gICAgTUlOSU1VTV9QUkVDSVNJT04pXFxuXFxuICAoZGVmdW4gdHJhbnNmZXI6c3RyaW5nIChzZW5kZXI6c3RyaW5nIHJlY2VpdmVyOnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBzZW5kZXIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgcmVjZWl2ZXIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpIF1cXG5cXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciByZWNlaXZlcilcXG4gICAgICBcXFwic2VuZGVyIGNhbm5vdCBiZSB0aGUgcmVjZWl2ZXIgb2YgYSB0cmFuc2ZlclxcXCIpXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcInRyYW5zZmVyIGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFRSQU5TRkVSIHNlbmRlciByZWNlaXZlciBhbW91bnQpXFxuICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIHJlY2VpdmVyXFxuICAgICAgICB7IFxcXCJndWFyZFxcXCIgOj0gZyB9XFxuXFxuICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIGcgYW1vdW50KSlcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biB0cmFuc2Zlci1jcmVhdGU6c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgYW1vdW50OmRlY2ltYWwgKVxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgY29uc2VydmVzLW1hc3MpIF1cXG5cXG4gICAgKGVuZm9yY2UgKCE9IHNlbmRlciByZWNlaXZlcilcXG4gICAgICBcXFwic2VuZGVyIGNhbm5vdCBiZSB0aGUgcmVjZWl2ZXIgb2YgYSB0cmFuc2ZlclxcXCIpXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcInRyYW5zZmVyIGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKFRSQU5TRkVSIHNlbmRlciByZWNlaXZlciBhbW91bnQpXFxuICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuICAgICAgKGNyZWRpdCByZWNlaXZlciByZWNlaXZlci1ndWFyZCBhbW91bnQpKVxcbiAgICApXFxuXFxuICAoZGVmdW4gY29pbmJhc2U6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhY2NvdW50LWd1YXJkOmd1YXJkIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJJbnRlcm5hbCBmdW5jdGlvbiBmb3IgdGhlIGluaXRpYWwgY3JlYXRpb24gb2YgY29pbnMuICBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgXFxcXGNhbm5vdCBiZSB1c2VkIG91dHNpZGUgb2YgdGhlIGNvaW4gY29udHJhY3QuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENPSU5CQVNFKSlcXG4gICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIGFjY291bnQgYW1vdW50KSkgO3YzXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIGFjY291bnQgYW1vdW50KSkgO3YzXFxuICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuXFxuICAgICAgKGVuZm9yY2UgKDw9IGFtb3VudCBiYWxhbmNlKSBcXFwiSW5zdWZmaWNpZW50IGZ1bmRzXFxcIilcXG5cXG4gICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAoLSBiYWxhbmNlIGFtb3VudCkgfVxcbiAgICAgICAgKSlcXG4gICAgKVxcblxcbiAgKGRlZnBhY3QgZnVuZC10eCAoc2VuZGVyOnN0cmluZyBtaW5lcjpzdHJpbmcgbWluZXItZ3VhcmQ6Z3VhcmQgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiJ2Z1bmQtdHgnIGlzIGEgc3BlY2lhbCBwYWN0IHRvIGZ1bmQgYSB0cmFuc2FjdGlvbiBpbiB0d28gc3RlcHMsICAgICBcXFxcXFxuICAgIFxcXFx3aXRoIHRoZSBhY3R1YWwgdHJhbnNhY3Rpb24gdHJhbnNwaXJpbmcgaW4gdGhlIG1pZGRsZTogICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxcXFxcXG4gICAgXFxcXCAgMSkgQSBidXlpbmcgcGhhc2UsIGRlYml0aW5nIHRoZSBzZW5kZXIgZm9yIHRvdGFsIGdhcyBhbmQgZmVlLCB5aWVsZGluZyBcXFxcXFxuICAgIFxcXFwgICAgIFRYX01BWF9DSEFSR0UuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAyKSBBIHNldHRsZW1lbnQgcGhhc2UsIHJlc3VtaW5nIFRYX01BWF9DSEFSR0UsIGFuZCBhbGxvY2F0aW5nIHRvIHRoZSAgIFxcXFxcXG4gICAgXFxcXCAgICAgY29pbmJhc2UgYWNjb3VudCBmb3IgdXNlZCBnYXMgYW5kIGZlZSwgYW5kIHNlbmRlciBhY2NvdW50IGZvciBiYWwtICBcXFxcXFxuICAgIFxcXFwgICAgIGFuY2UgKHVudXNlZCBnYXMsIGlmIGFueSkuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICAgIDsocHJvcGVydHkgY29uc2VydmVzLW1hc3MpIG5vdCBzdXBwb3J0ZWQgeWV0XFxuICAgICAgICAgICBdXFxuXFxuICAgIChzdGVwIChidXktZ2FzIHNlbmRlciB0b3RhbCkpXFxuICAgIChzdGVwIChyZWRlZW0tZ2FzIG1pbmVyIG1pbmVyLWd1YXJkIHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZWJpdDpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJEZWJpdCBBTU9VTlQgZnJvbSBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJkZWJpdCBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChERUJJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICAoZGVmdW4gY3JlZGl0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgZ3VhcmQ6Z3VhcmQgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkNyZWRpdCBBTU9VTlQgdG8gQUNDT1VOVCBiYWxhbmNlXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMCkgXFxcImNyZWRpdCBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDUkVESVQgYWNjb3VudCkpXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IC0xLjAsIFxcXCJndWFyZFxcXCIgOiBndWFyZCB9XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSwgXFxcImd1YXJkXFxcIiA6PSByZXRnIH1cXG4gICAgICA7IHdlIGRvbid0IHdhbnQgdG8gb3ZlcndyaXRlIGFuIGV4aXN0aW5nIGd1YXJkIHdpdGggdGhlIHVzZXItc3VwcGxpZWQgb25lXFxuICAgICAgKGVuZm9yY2UgKD0gcmV0ZyBndWFyZClcXG4gICAgICAgIFxcXCJhY2NvdW50IGd1YXJkcyBkbyBub3QgbWF0Y2hcXFwiKVxcblxcbiAgICAgIChsZXQgKChpcy1uZXdcXG4gICAgICAgICAgICAgKGlmICg9IGJhbGFuY2UgLTEuMClcXG4gICAgICAgICAgICAgICAgIChlbmZvcmNlLXJlc2VydmVkIGFjY291bnQgZ3VhcmQpXFxuICAgICAgICAgICAgICAgZmFsc2UpKSlcXG5cXG4gICAgICAgICh3cml0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAoaWYgaXMtbmV3IGFtb3VudCAoKyBiYWxhbmNlIGFtb3VudCkpXFxuICAgICAgICAgICwgXFxcImd1YXJkXFxcIiAgIDogcmV0Z1xcbiAgICAgICAgICB9KSlcXG4gICAgICApKVxcblxcbiAgKGRlZnVuIGNoZWNrLXJlc2VydmVkOnN0cmluZyAoYWNjb3VudDpzdHJpbmcpXFxuICAgIFxcXCIgQ2hlY2tzIEFDQ09VTlQgZm9yIHJlc2VydmVkIG5hbWUgYW5kIHJldHVybnMgdHlwZSBpZiBcXFxcXFxuICAgIFxcXFwgZm91bmQgb3IgZW1wdHkgc3RyaW5nLiBSZXNlcnZlZCBuYW1lcyBzdGFydCB3aXRoIGEgXFxcXFxcbiAgICBcXFxcIHNpbmdsZSBjaGFyIGFuZCBjb2xvbiwgZS5nLiAnYzpmb28nLCB3aGljaCB3b3VsZCByZXR1cm4gJ2MnIGFzIHR5cGUuXFxcIlxcbiAgICAobGV0ICgocGZ4ICh0YWtlIDIgYWNjb3VudCkpKVxcbiAgICAgIChpZiAoPSBcXFwiOlxcXCIgKHRha2UgLTEgcGZ4KSkgKHRha2UgMSBwZngpIFxcXCJcXFwiKSkpXFxuXFxuICAoZGVmdW4gZW5mb3JjZS1yZXNlcnZlZDpib29sIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSByZXNlcnZlZCBhY2NvdW50IG5hbWUgcHJvdG9jb2xzLlxcXCJcXG4gICAgKGlmICh2YWxpZGF0ZS1wcmluY2lwYWwgZ3VhcmQgYWNjb3VudClcXG4gICAgICB0cnVlXFxuICAgICAgKGxldCAoKHIgKGNoZWNrLXJlc2VydmVkIGFjY291bnQpKSlcXG4gICAgICAgIChpZiAoPSByIFxcXCJcXFwiKVxcbiAgICAgICAgICB0cnVlXFxuICAgICAgICAgIChpZiAoPSByIFxcXCJrXFxcIilcXG4gICAgICAgICAgICAoZW5mb3JjZSBmYWxzZSBcXFwiU2luZ2xlLWtleSBhY2NvdW50IHByb3RvY29sIHZpb2xhdGlvblxcXCIpXFxuICAgICAgICAgICAgKGVuZm9yY2UgZmFsc2VcXG4gICAgICAgICAgICAgIChmb3JtYXQgXFxcIlJlc2VydmVkIHByb3RvY29sIGd1YXJkIHZpb2xhdGlvbjoge31cXFwiIFtyXSkpXFxuICAgICAgICAgICAgKSkpKSlcXG5cXG5cXG4gIChkZWZzY2hlbWEgY3Jvc3NjaGFpbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiU2NoZW1hIGZvciB5aWVsZGVkIHZhbHVlIGluIGNyb3NzLWNoYWluIHRyYW5zZmVyc1xcXCJcXG4gICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIHNvdXJjZS1jaGFpbjpzdHJpbmcpXFxuXFxuICAoZGVmcGFjdCB0cmFuc2Zlci1jcm9zc2NoYWluOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbCApXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgIChzdGVwXFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eVxcbiAgICAgICAgKFRSQU5TRkVSX1hDSEFJTiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50IHRhcmdldC1jaGFpbilcXG5cXG4gICAgICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKCE9IFxcXCJcXFwiIHRhcmdldC1jaGFpbikgXFxcImVtcHR5IHRhcmdldC1jaGFpblxcXCIpXFxuICAgICAgICAoZW5mb3JjZSAoIT0gKGF0ICdjaGFpbi1pZCAoY2hhaW4tZGF0YSkpIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgXFxcImNhbm5vdCBydW4gY3Jvc3MtY2hhaW4gdHJhbnNmZXJzIHRvIHRoZSBzYW1lIGNoYWluXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgICAgIFxcXCJ0cmFuc2ZlciBxdWFudGl0eSBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAgICAgOzsgc3RlcCAxIC0gZGViaXQgZGVsZXRlLWFjY291bnQgb24gY3VycmVudCBjaGFpblxcbiAgICAgICAgKGRlYml0IHNlbmRlciBhbW91bnQpXFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgc2VuZGVyIFxcXCJcXFwiIGFtb3VudCkpXFxuXFxuICAgICAgICAobGV0XFxuICAgICAgICAgICgoY3Jvc3NjaGFpbi1kZXRhaWxzOm9iamVjdHtjcm9zc2NoYWluLXNjaGVtYX1cXG4gICAgICAgICAgICB7IFxcXCJyZWNlaXZlclxcXCIgOiByZWNlaXZlclxcbiAgICAgICAgICAgICwgXFxcInJlY2VpdmVyLWd1YXJkXFxcIiA6IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAgICAgLCBcXFwiYW1vdW50XFxcIiA6IGFtb3VudFxcbiAgICAgICAgICAgICwgXFxcInNvdXJjZS1jaGFpblxcXCIgOiAoYXQgJ2NoYWluLWlkIChjaGFpbi1kYXRhKSlcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcbiAgICAgICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIHJlY2VpdmVyIGFtb3VudCkpXFxuICAgICAgICA7OyBzdGVwIDIgLSBjcmVkaXQgY3JlYXRlIGFjY291bnQgb24gdGFyZ2V0IGNoYWluXFxuICAgICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpXFxuICAgICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgQ29pbiBhbGxvY2F0aW9uc1xcblxcbiAgKGRlZnNjaGVtYSBhbGxvY2F0aW9uLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJHZW5lc2lzIGFsbG9jYXRpb24gcmVnaXN0cnlcXFwiXFxuICAgIDtAbW9kZWwgWyAoaW52YXJpYW50ICg-PSBiYWxhbmNlIDAuMCkpIF1cXG5cXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGRhdGU6dGltZVxcbiAgICBndWFyZDpndWFyZFxcbiAgICByZWRlZW1lZDpib29sKVxcblxcbiAgKGRlZnRhYmxlIGFsbG9jYXRpb24tdGFibGU6e2FsbG9jYXRpb24tc2NoZW1hfSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50XFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBkYXRlOnRpbWVcXG4gICAgICBrZXlzZXQtcmVmOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgQGRvYyBcXFwiQWRkIGFuIGVudHJ5IHRvIHRoZSBjb2luIGFsbG9jYXRpb24gdGFibGUuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxhbHNvIGNyZWF0ZXMgYSBjb3JyZXNwb25kaW5nIGVtcHR5IGNvaW4gY29udHJhY3QgYWNjb3VudCBcXFxcXFxuICAgICAgICAgXFxcXG9mIHRoZSBzYW1lIG5hbWUgYW5kIGd1YXJkLiBSZXF1aXJlcyBHRU5FU0lTIGNhcGFiaWxpdHkuIFxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdFTkVTSVMpKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZSAoPj0gYW1vdW50IDAuMClcXG4gICAgICBcXFwiYWxsb2NhdGlvbiBhbW91bnQgbXVzdCBiZSBub24tbmVnYXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKGxldFxcbiAgICAgICgoZ3VhcmQ6Z3VhcmQgKGtleXNldC1yZWYtZ3VhcmQga2V5c2V0LXJlZikpKVxcblxcbiAgICAgIChjcmVhdGUtYWNjb3VudCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAgIChpbnNlcnQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IGFtb3VudFxcbiAgICAgICAgLCBcXFwiZGF0ZVxcXCIgOiBkYXRlXFxuICAgICAgICAsIFxcXCJndWFyZFxcXCIgOiBndWFyZFxcbiAgICAgICAgLCBcXFwicmVkZWVtZWRcXFwiIDogZmFsc2VcXG4gICAgICAgIH0pKSlcXG5cXG4gIChkZWZ1biByZWxlYXNlLWFsbG9jYXRpb25cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuXFxuICAgIEBkb2MgXFxcIlJlbGVhc2UgZnVuZHMgYXNzb2NpYXRlZCB3aXRoIGFsbG9jYXRpb24gQUNDT1VOVCBpbnRvIG1haW4gbGVkZ2VyLiAgIFxcXFxcXG4gICAgICAgICBcXFxcQUNDT1VOVCBtdXN0IGFscmVhZHkgZXhpc3QgaW4gbWFpbiBsZWRnZXIuIEFsbG9jYXRpb24gaXMgZGVhY3RpdmF0ZWQgXFxcXFxcbiAgICAgICAgIFxcXFxhZnRlciByZWxlYXNlLlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgICh3aXRoLXJlYWQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZVxcbiAgICAgICwgXFxcImRhdGVcXFwiIDo9IHJlbGVhc2UtdGltZVxcbiAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6PSByZWRlZW1lZFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBndWFyZFxcbiAgICAgIH1cXG5cXG4gICAgICAobGV0ICgoY3Vyci10aW1lOnRpbWUgKGF0ICdibG9jay10aW1lIChjaGFpbi1kYXRhKSkpKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKG5vdCByZWRlZW1lZClcXG4gICAgICAgICAgXFxcImFsbG9jYXRpb24gZnVuZHMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVkZWVtZWRcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2VcXG4gICAgICAgICAgKD49IGN1cnItdGltZSByZWxlYXNlLXRpbWUpXFxuICAgICAgICAgIChmb3JtYXQgXFxcImZ1bmRzIGxvY2tlZCB1bnRpbCB7fS4gY3VycmVudCB0aW1lOiB7fVxcXCIgW3JlbGVhc2UtdGltZSBjdXJyLXRpbWVdKSlcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKFJFTEVBU0VfQUxMT0NBVElPTiBhY2NvdW50IGJhbGFuY2UpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBiYWxhbmNlKSlcXG4gICAgICAgICAgKGNyZWRpdCBhY2NvdW50IGd1YXJkIGJhbGFuY2UpXFxuXFxuICAgICAgICAgICh1cGRhdGUgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgICAgeyBcXFwicmVkZWVtZWRcXFwiIDogdHJ1ZVxcbiAgICAgICAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgICAgICAgfSlcXG5cXG4gICAgICAgICAgXFxcIkFsbG9jYXRpb24gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIHRvIG1haW4gbGVkZ2VyXFxcIikpXFxuICAgICkpKVxcblxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImNvaW4tY29udHJhY3QtdjRcIn0ifQ" diff --git a/src/Chainweb/Pact/Transactions/CoinV5Transactions.hs b/src/Chainweb/Pact/Transactions/CoinV5Transactions.hs index 6e9d8670be..39c9d259a5 100644 --- a/src/Chainweb/Pact/Transactions/CoinV5Transactions.hs +++ b/src/Chainweb/Pact/Transactions/CoinV5Transactions.hs @@ -5,14 +5,15 @@ module Chainweb.Pact.Transactions.CoinV5Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiOERDei1xb2pVcWUyRTJ6R1V1clhuanBJUHlxSFVlcFlmdFdockhUZVd3SSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG4gIChpbXBsZW1lbnRzIGZ1bmdpYmxlLXhjaGFpbi12MSlcXG5cXG4gIDs7IGNvaW4tdjJcXG4gIChibGVzcyBcXFwidXRfSl9aTmtveWFQVUVKaGl3VmVXbmtTUW45SlQ5c1FDV0tkampWVnJXb1xcXCIpXFxuXFxuICA7OyBjb2luIHYzXFxuICAoYmxlc3MgXFxcIjFvc19zTEFVWXZCenNwbjVqamF3dFJwSldpSDFXUGZoeU5yYWVWdlNJd1VcXFwiKVxcblxcbiAgOzsgY29pbiB2NFxcbiAgKGJsZXNzIFxcXCJCalpXMFQyYWM2cUVfSTVYOEdFNGZhbDZ0VHFqaExUQzdteTB5dFFTeExVXFxcIilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICApXFxuXFxuICAgIEBtYW5hZ2VkIGFtb3VudCBUUkFOU0ZFUl9YQ0hBSU4tbWdyXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiQ3Jvc3MtY2hhaW4gdHJhbnNmZXJzIHJlcXVpcmUgYSBwb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgKGVuZm9yY2UgKD49IG1hbmFnZWQgcmVxdWVzdGVkKVxcbiAgICAgIChmb3JtYXQgXFxcIlRSQU5TRkVSX1hDSEFJTiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgMC4wXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTl9SRUNEOmJvb2xcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgICBzb3VyY2UtY2hhaW46c3RyaW5nXFxuICAgIClcXG4gICAgQGV2ZW50IHRydWVcXG4gIClcXG5cXG4gIDsgdjMgY2FwYWJpbGl0aWVzXFxuICAoZGVmY2FwIFJFTEVBU0VfQUxMT0NBVElPTlxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgKVxcbiAgICBAZG9jIFxcXCJFdmVudCBmb3IgYWxsb2NhdGlvbiByZWxlYXNlLCBjYW4gYmUgdXNlZCBmb3Igc2lnIHNjb3BpbmcuXFxcIlxcbiAgICBAZXZlbnQgdHJ1ZVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb25zdGFudHNcXG5cXG4gIChkZWZjb25zdCBDT0lOX0NIQVJTRVQgQ0hBUlNFVF9MQVRJTjFcXG4gICAgXFxcIlRoZSBkZWZhdWx0IGNvaW4gY29udHJhY3QgY2hhcmFjdGVyIHNldFxcXCIpXFxuXFxuICAoZGVmY29uc3QgTUlOSU1VTV9QUkVDSVNJT04gMTJcXG4gICAgXFxcIk1pbmltdW0gYWxsb3dlZCBwcmVjaXNpb24gZm9yIGNvaW4gdHJhbnNhY3Rpb25zXFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIIDNcXG4gICAgXFxcIk1pbmltdW0gYWNjb3VudCBsZW5ndGggYWRtaXNzaWJsZSBmb3IgY29pbiBhY2NvdW50c1xcXCIpXFxuXFxuICAoZGVmY29uc3QgTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSCAyNTZcXG4gICAgXFxcIk1heGltdW0gYWNjb3VudCBuYW1lIGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBWQUxJRF9DSEFJTl9JRFMgKG1hcCAoaW50LXRvLXN0ciAxMCkgKGVudW1lcmF0ZSAwIDE5KSlcXG4gICAgXFxcIkxpc3Qgb2YgYWxsIHZhbGlkIENoYWlud2ViIGNoYWluIGlkc1xcXCIpXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IFV0aWxpdGllc1xcblxcbiAgKGRlZnVuIGVuZm9yY2UtdW5pdDpib29sIChhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSBtaW5pbXVtIHByZWNpc2lvbiBhbGxvd2VkIGZvciBjb2luIHRyYW5zYWN0aW9uc1xcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoPSAoZmxvb3IgYW1vdW50IE1JTklNVU1fUFJFQ0lTSU9OKVxcbiAgICAgICAgIGFtb3VudClcXG4gICAgICAoZm9ybWF0IFxcXCJBbW91bnQgdmlvbGF0ZXMgbWluaW11bSBwcmVjaXNpb246IHt9XFxcIiBbYW1vdW50XSkpXFxuICAgIClcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZS1hY2NvdW50IChhY2NvdW50OnN0cmluZylcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSB0aGF0IGFuIGFjY291bnQgbmFtZSBjb25mb3JtcyB0byB0aGUgY29pbiBjb250cmFjdCBcXFxcXFxuICAgICAgICAgXFxcXG1pbmltdW0gYW5kIG1heGltdW0gbGVuZ3RoIHJlcXVpcmVtZW50cywgYXMgd2VsbCBhcyB0aGUgICAgXFxcXFxcbiAgICAgICAgIFxcXFxsYXRpbi0xIGNoYXJhY3RlciBzZXQuXFxcIlxcblxcbiAgICAoZW5mb3JjZVxcbiAgICAgIChpcy1jaGFyc2V0IENPSU5fQ0hBUlNFVCBhY2NvdW50KVxcbiAgICAgIChmb3JtYXRcXG4gICAgICAgIFxcXCJBY2NvdW50IGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIGNvaW4gY29udHJhY3QgY2hhcnNldDoge31cXFwiXFxuICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAobGV0ICgoYWNjb3VudC1sZW5ndGggKGxlbmd0aCBhY2NvdW50KSkpXFxuXFxuICAgICAgKGVuZm9yY2VcXG4gICAgICAgICg-PSBhY2NvdW50LWxlbmd0aCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIKVxcbiAgICAgICAgKGZvcm1hdFxcbiAgICAgICAgICBcXFwiQWNjb3VudCBuYW1lIGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIG1pbiBsZW5ndGggcmVxdWlyZW1lbnQ6IHt9XFxcIlxcbiAgICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPD0gYWNjb3VudC1sZW5ndGggTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtYXggbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG4gICAgICApXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gQ29udHJhY3RcXG5cXG4gIChkZWZ1biBnYXMtb25seSAoKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMtb25seSB1c2VyIGd1YXJkcy5cXFwiXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpKVxcblxcbiAgKGRlZnVuIGdhcy1ndWFyZCAoZ3VhcmQ6Z3VhcmQpXFxuICAgIFxcXCJQcmVkaWNhdGUgZm9yIGdhcyArIHNpbmdsZSBrZXkgdXNlciBndWFyZHNcXFwiXFxuICAgIChlbmZvcmNlLW9uZVxcbiAgICAgIFxcXCJFbmZvcmNlIGVpdGhlciB0aGUgcHJlc2VuY2Ugb2YgYSBHQVMgY2FwIG9yIGtleXNldFxcXCJcXG4gICAgICBbIChnYXMtb25seSlcXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIGd1YXJkKVxcbiAgICAgIF0pKVxcblxcbiAgKGRlZnVuIGJ1eS1nYXM6c3RyaW5nIChzZW5kZXI6c3RyaW5nIHRvdGFsOmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIlRoaXMgZnVuY3Rpb24gZGVzY3JpYmVzIHRoZSBtYWluICdnYXMgYnV5JyBvcGVyYXRpb24uIEF0IHRoaXMgcG9pbnQgXFxcXFxcbiAgICBcXFxcTUlORVIgaGFzIGJlZW4gY2hvc2VuIGZyb20gdGhlIHBvb2wsIGFuZCB3aWxsIGJlIHZhbGlkYXRlZC4gVGhlIFNFTkRFUiAgIFxcXFxcXG4gICAgXFxcXG9mIHRoaXMgdHJhbnNhY3Rpb24gaGFzIHNwZWNpZmllZCBhIGdhcyBsaW1pdCBMSU1JVCAobWF4aW11bSBnYXMpIGZvciAgICBcXFxcXFxuICAgIFxcXFx0aGUgdHJhbnNhY3Rpb24sIGFuZCB0aGUgcHJpY2UgaXMgdGhlIHNwb3QgcHJpY2Ugb2YgZ2FzIGF0IHRoYXQgdGltZS4gICAgXFxcXFxcbiAgICBcXFxcVGhlIGdhcyBidXkgd2lsbCBiZSBleGVjdXRlZCBwcmlvciB0byBleGVjdXRpbmcgU0VOREVSJ3MgY29kZS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcblxcbiAgICAoZW5mb3JjZS11bml0IHRvdGFsKVxcbiAgICAoZW5mb3JjZSAoPiB0b3RhbCAwLjApIFxcXCJnYXMgc3VwcGx5IG11c3QgYmUgYSBwb3NpdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcilcXG4gICAgICAoZGViaXQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlZGVlbS1nYXM6c3RyaW5nIChtaW5lcjpzdHJpbmcgbWluZXItZ3VhcmQ6Z3VhcmQgc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAncmVkZWVtIGdhcycgb3BlcmF0aW9uLiBBdCB0aGlzICAgIFxcXFxcXG4gICAgXFxcXHBvaW50LCB0aGUgU0VOREVSJ3MgdHJhbnNhY3Rpb24gaGFzIGJlZW4gZXhlY3V0ZWQsIGFuZCB0aGUgZ2FzIHRoYXQgICAgICBcXFxcXFxuICAgIFxcXFx3YXMgY2hhcmdlZCBoYXMgYmVlbiBjYWxjdWxhdGVkLiBNSU5FUiB3aWxsIGJlIGNyZWRpdGVkIHRoZSBnYXMgY29zdCwgICAgXFxcXFxcbiAgICBcXFxcYW5kIFNFTkRFUiB3aWxsIHJlY2VpdmUgdGhlIHJlbWFpbmRlciB1cCB0byB0aGUgbGltaXRcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBtaW5lcilcXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoR0FTKSlcXG4gICAgKGxldCpcXG4gICAgICAoKGZlZSAocmVhZC1kZWNpbWFsIFxcXCJmZWVcXFwiKSlcXG4gICAgICAgKHJlZnVuZCAoLSB0b3RhbCBmZWUpKSlcXG5cXG4gICAgICAoZW5mb3JjZS11bml0IGZlZSlcXG4gICAgICAoZW5mb3JjZSAoPj0gZmVlIDAuMClcXG4gICAgICAgIFxcXCJmZWUgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgICAgKGVuZm9yY2UgKD49IHJlZnVuZCAwLjApXFxuICAgICAgICBcXFwicmVmdW5kIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgbWluZXIgZmVlKSkgO3YzXFxuXFxuICAgICAgICA7IGRpcmVjdGx5IHVwZGF0ZSBpbnN0ZWFkIG9mIGNyZWRpdFxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBzZW5kZXIpXFxuICAgICAgICAoaWYgKD4gcmVmdW5kIDAuMClcXG4gICAgICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgICAgICAgICh1cGRhdGUgY29pbi10YWJsZSBzZW5kZXJcXG4gICAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiOiAoKyBiYWxhbmNlIHJlZnVuZCkgfSkpXFxuXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuXFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIG1pbmVyKVxcbiAgICAgICAgKGlmICg-IGZlZSAwLjApXFxuICAgICAgICAgIChjcmVkaXQgbWluZXIgbWluZXItZ3VhcmQgZmVlKVxcbiAgICAgICAgICBcXFwibm9vcFxcXCIpKVxcbiAgICAgIClcXG5cXG4gICAgKVxcblxcbiAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgZ3VhcmQ6Z3VhcmQpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS1yZXNlcnZlZCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAoaW5zZXJ0IGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiBndWFyZFxcbiAgICAgIH0pXFxuICAgIClcXG5cXG4gIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsIChhY2NvdW50OnN0cmluZylcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG4gICAgICBiYWxhbmNlXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gZGV0YWlsczpvYmplY3R7ZnVuZ2libGUtdjIuYWNjb3VudC1kZXRhaWxzfVxcbiAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZyB9XFxuICAgICAgeyBcXFwiYWNjb3VudFxcXCIgOiBhY2NvdW50XFxuICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCI6IGcgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJvdGF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIG5ldy1ndWFyZDpndWFyZClcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoUk9UQVRFIGFjY291bnQpXFxuICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBvbGQtZ3VhcmQgfVxcblxcbiAgICAgICAgKGVuZm9yY2UtZ3VhcmQgb2xkLWd1YXJkKVxcblxcbiAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDogbmV3LWd1YXJkIH1cXG4gICAgICAgICAgKSkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBwcmVjaXNpb246aW50ZWdlclxcbiAgICAoKVxcbiAgICBNSU5JTVVNX1BSRUNJU0lPTilcXG5cXG4gIChkZWZ1biB0cmFuc2ZlcjpzdHJpbmcgKHNlbmRlcjpzdHJpbmcgcmVjZWl2ZXI6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgY29uc2VydmVzLW1hc3MpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCByZWNlaXZlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSkgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgcmVjZWl2ZXJcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG5cXG4gICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgZyBhbW91bnQpKVxcbiAgICAgIClcXG4gICAgKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyLWNyZWF0ZTpzdHJpbmdcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICBhbW91bnQ6ZGVjaW1hbCApXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biBjb2luYmFzZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFjY291bnQtZ3VhcmQ6Z3VhcmQgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkludGVybmFsIGZ1bmN0aW9uIGZvciB0aGUgaW5pdGlhbCBjcmVhdGlvbiBvZiBjb2lucy4gIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICBcXFxcY2Fubm90IGJlIHVzZWQgb3V0c2lkZSBvZiB0aGUgY29pbiBjb250cmFjdC5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoQ09JTkJBU0UpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIGFjY291bnQpXFxuICAgICAgKGNyZWRpdCBhY2NvdW50IGFjY291bnQtZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlbWVkaWF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJBbGxvd3MgZm9yIHJlbWVkaWF0aW9uIHRyYW5zYWN0aW9ucy4gVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgICAgICAgXFxcXGlzIHByb3RlY3RlZCBieSB0aGUgUkVNRURJQVRFIGNhcGFiaWxpdHlcXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJSZW1lZGlhdGlvbiBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChSRU1FRElBVEUpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogLTEuMCwgXFxcImd1YXJkXFxcIiA6IGd1YXJkIH1cXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlLCBcXFwiZ3VhcmRcXFwiIDo9IHJldGcgfVxcbiAgICAgIDsgd2UgZG9uJ3Qgd2FudCB0byBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgZ3VhcmQgd2l0aCB0aGUgdXNlci1zdXBwbGllZCBvbmVcXG4gICAgICAoZW5mb3JjZSAoPSByZXRnIGd1YXJkKVxcbiAgICAgICAgXFxcImFjY291bnQgZ3VhcmRzIGRvIG5vdCBtYXRjaFxcXCIpXFxuXFxuICAgICAgKGxldCAoKGlzLW5ld1xcbiAgICAgICAgICAgICAoaWYgKD0gYmFsYW5jZSAtMS4wKVxcbiAgICAgICAgICAgICAgICAgKGVuZm9yY2UtcmVzZXJ2ZWQgYWNjb3VudCBndWFyZClcXG4gICAgICAgICAgICAgICBmYWxzZSkpKVxcblxcbiAgICAgICAgKHdyaXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IChpZiBpcy1uZXcgYW1vdW50ICgrIGJhbGFuY2UgYW1vdW50KSlcXG4gICAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICAgIH0pKVxcbiAgICAgICkpXFxuXFxuICAoZGVmdW4gY2hlY2stcmVzZXJ2ZWQ6c3RyaW5nIChhY2NvdW50OnN0cmluZylcXG4gICAgXFxcIiBDaGVja3MgQUNDT1VOVCBmb3IgcmVzZXJ2ZWQgbmFtZSBhbmQgcmV0dXJucyB0eXBlIGlmIFxcXFxcXG4gICAgXFxcXCBmb3VuZCBvciBlbXB0eSBzdHJpbmcuIFJlc2VydmVkIG5hbWVzIHN0YXJ0IHdpdGggYSBcXFxcXFxuICAgIFxcXFwgc2luZ2xlIGNoYXIgYW5kIGNvbG9uLCBlLmcuICdjOmZvbycsIHdoaWNoIHdvdWxkIHJldHVybiAnYycgYXMgdHlwZS5cXFwiXFxuICAgIChsZXQgKChwZnggKHRha2UgMiBhY2NvdW50KSkpXFxuICAgICAgKGlmICg9IFxcXCI6XFxcIiAodGFrZSAtMSBwZngpKSAodGFrZSAxIHBmeCkgXFxcIlxcXCIpKSlcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXJlc2VydmVkOmJvb2wgKGFjY291bnQ6c3RyaW5nIGd1YXJkOmd1YXJkKVxcbiAgICBAZG9jIFxcXCJFbmZvcmNlIHJlc2VydmVkIGFjY291bnQgbmFtZSBwcm90b2NvbHMuXFxcIlxcbiAgICAoaWYgKHZhbGlkYXRlLXByaW5jaXBhbCBndWFyZCBhY2NvdW50KVxcbiAgICAgIHRydWVcXG4gICAgICAobGV0ICgociAoY2hlY2stcmVzZXJ2ZWQgYWNjb3VudCkpKVxcbiAgICAgICAgKGlmICg9IHIgXFxcIlxcXCIpXFxuICAgICAgICAgIHRydWVcXG4gICAgICAgICAgKGlmICg9IHIgXFxcImtcXFwiKVxcbiAgICAgICAgICAgIChlbmZvcmNlIGZhbHNlIFxcXCJTaW5nbGUta2V5IGFjY291bnQgcHJvdG9jb2wgdmlvbGF0aW9uXFxcIilcXG4gICAgICAgICAgICAoZW5mb3JjZSBmYWxzZVxcbiAgICAgICAgICAgICAgKGZvcm1hdCBcXFwiUmVzZXJ2ZWQgcHJvdG9jb2wgZ3VhcmQgdmlvbGF0aW9uOiB7fVxcXCIgW3JdKSlcXG4gICAgICAgICAgICApKSkpKVxcblxcblxcbiAgKGRlZnNjaGVtYSBjcm9zc2NoYWluLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHlpZWxkZWQgdmFsdWUgaW4gY3Jvc3MtY2hhaW4gdHJhbnNmZXJzXFxcIlxcbiAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgYW1vdW50OmRlY2ltYWxcXG4gICAgc291cmNlLWNoYWluOnN0cmluZylcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5XFxuICAgICAgICAoVFJBTlNGRVJfWENIQUlOIHNlbmRlciByZWNlaXZlciBhbW91bnQgdGFyZ2V0LWNoYWluKVxcblxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoIT0gXFxcIlxcXCIgdGFyZ2V0LWNoYWluKSBcXFwiZW1wdHkgdGFyZ2V0LWNoYWluXFxcIilcXG4gICAgICAgIChlbmZvcmNlICghPSAoYXQgJ2NoYWluLWlkIChjaGFpbi1kYXRhKSkgdGFyZ2V0LWNoYWluKVxcbiAgICAgICAgICBcXFwiY2Fubm90IHJ1biBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdG8gdGhlIHNhbWUgY2hhaW5cXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICAgICAgXFxcInRyYW5zZmVyIHF1YW50aXR5IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoY29udGFpbnMgdGFyZ2V0LWNoYWluIFZBTElEX0NIQUlOX0lEUylcXG4gICAgICAgICAgXFxcInRhcmdldCBjaGFpbiBpcyBub3QgYSB2YWxpZCBjaGFpbndlYiBjaGFpbiBpZFxcXCIpXFxuXFxuICAgICAgICA7OyBzdGVwIDEgLSBkZWJpdCBkZWxldGUtYWNjb3VudCBvbiBjdXJyZW50IGNoYWluXFxuICAgICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgXFxcIlxcXCIgYW1vdW50KSlcXG5cXG4gICAgICAgIChsZXRcXG4gICAgICAgICAgKChjcm9zc2NoYWluLWRldGFpbHM6b2JqZWN0e2Nyb3NzY2hhaW4tc2NoZW1hfVxcbiAgICAgICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6IHJlY2VpdmVyXFxuICAgICAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDogcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDogYW1vdW50XFxuICAgICAgICAgICAgLCBcXFwic291cmNlLWNoYWluXFxcIiA6IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKVxcbiAgICAgICAgICAgIH0pKVxcbiAgICAgICAgICAoeWllbGQgY3Jvc3NjaGFpbi1kZXRhaWxzIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgKSkpXFxuXFxuICAgIChzdGVwXFxuICAgICAgKHJlc3VtZVxcbiAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDo9IHJlY2VpdmVyXFxuICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOj0gcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICwgXFxcImFtb3VudFxcXCIgOj0gYW1vdW50XFxuICAgICAgICAsIFxcXCJzb3VyY2UtY2hhaW5cXFwiIDo9IHNvdXJjZS1jaGFpblxcbiAgICAgICAgfVxcblxcbiAgICAgICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIHJlY2VpdmVyIGFtb3VudCkpXFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVJfWENIQUlOX1JFQ0QgXFxcIlxcXCIgcmVjZWl2ZXIgYW1vdW50IHNvdXJjZS1jaGFpbikpXFxuXFxuICAgICAgICA7OyBzdGVwIDIgLSBjcmVkaXQgY3JlYXRlIGFjY291bnQgb24gdGFyZ2V0IGNoYWluXFxuICAgICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpXFxuICAgICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgQ29pbiBhbGxvY2F0aW9uc1xcblxcbiAgKGRlZnNjaGVtYSBhbGxvY2F0aW9uLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJHZW5lc2lzIGFsbG9jYXRpb24gcmVnaXN0cnlcXFwiXFxuICAgIDtAbW9kZWwgWyAoaW52YXJpYW50ICg-PSBiYWxhbmNlIDAuMCkpIF1cXG5cXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGRhdGU6dGltZVxcbiAgICBndWFyZDpndWFyZFxcbiAgICByZWRlZW1lZDpib29sKVxcblxcbiAgKGRlZnRhYmxlIGFsbG9jYXRpb24tdGFibGU6e2FsbG9jYXRpb24tc2NoZW1hfSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50XFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBkYXRlOnRpbWVcXG4gICAgICBrZXlzZXQtcmVmOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgQGRvYyBcXFwiQWRkIGFuIGVudHJ5IHRvIHRoZSBjb2luIGFsbG9jYXRpb24gdGFibGUuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxhbHNvIGNyZWF0ZXMgYSBjb3JyZXNwb25kaW5nIGVtcHR5IGNvaW4gY29udHJhY3QgYWNjb3VudCBcXFxcXFxuICAgICAgICAgXFxcXG9mIHRoZSBzYW1lIG5hbWUgYW5kIGd1YXJkLiBSZXF1aXJlcyBHRU5FU0lTIGNhcGFiaWxpdHkuIFxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdFTkVTSVMpKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZSAoPj0gYW1vdW50IDAuMClcXG4gICAgICBcXFwiYWxsb2NhdGlvbiBhbW91bnQgbXVzdCBiZSBub24tbmVnYXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKGxldFxcbiAgICAgICgoZ3VhcmQ6Z3VhcmQgKGtleXNldC1yZWYtZ3VhcmQga2V5c2V0LXJlZikpKVxcblxcbiAgICAgIChjcmVhdGUtYWNjb3VudCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAgIChpbnNlcnQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IGFtb3VudFxcbiAgICAgICAgLCBcXFwiZGF0ZVxcXCIgOiBkYXRlXFxuICAgICAgICAsIFxcXCJndWFyZFxcXCIgOiBndWFyZFxcbiAgICAgICAgLCBcXFwicmVkZWVtZWRcXFwiIDogZmFsc2VcXG4gICAgICAgIH0pKSlcXG5cXG4gIChkZWZ1biByZWxlYXNlLWFsbG9jYXRpb25cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuXFxuICAgIEBkb2MgXFxcIlJlbGVhc2UgZnVuZHMgYXNzb2NpYXRlZCB3aXRoIGFsbG9jYXRpb24gQUNDT1VOVCBpbnRvIG1haW4gbGVkZ2VyLiAgIFxcXFxcXG4gICAgICAgICBcXFxcQUNDT1VOVCBtdXN0IGFscmVhZHkgZXhpc3QgaW4gbWFpbiBsZWRnZXIuIEFsbG9jYXRpb24gaXMgZGVhY3RpdmF0ZWQgXFxcXFxcbiAgICAgICAgIFxcXFxhZnRlciByZWxlYXNlLlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgICh3aXRoLXJlYWQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZVxcbiAgICAgICwgXFxcImRhdGVcXFwiIDo9IHJlbGVhc2UtdGltZVxcbiAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6PSByZWRlZW1lZFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBndWFyZFxcbiAgICAgIH1cXG5cXG4gICAgICAobGV0ICgoY3Vyci10aW1lOnRpbWUgKGF0ICdibG9jay10aW1lIChjaGFpbi1kYXRhKSkpKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKG5vdCByZWRlZW1lZClcXG4gICAgICAgICAgXFxcImFsbG9jYXRpb24gZnVuZHMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVkZWVtZWRcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2VcXG4gICAgICAgICAgKD49IGN1cnItdGltZSByZWxlYXNlLXRpbWUpXFxuICAgICAgICAgIChmb3JtYXQgXFxcImZ1bmRzIGxvY2tlZCB1bnRpbCB7fS4gY3VycmVudCB0aW1lOiB7fVxcXCIgW3JlbGVhc2UtdGltZSBjdXJyLXRpbWVdKSlcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKFJFTEVBU0VfQUxMT0NBVElPTiBhY2NvdW50IGJhbGFuY2UpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBiYWxhbmNlKSlcXG4gICAgICAgICAgKGNyZWRpdCBhY2NvdW50IGd1YXJkIGJhbGFuY2UpXFxuXFxuICAgICAgICAgICh1cGRhdGUgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgICAgeyBcXFwicmVkZWVtZWRcXFwiIDogdHJ1ZVxcbiAgICAgICAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgICAgICAgfSlcXG5cXG4gICAgICAgICAgXFxcIkFsbG9jYXRpb24gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIHRvIG1haW4gbGVkZ2VyXFxcIikpXFxuICAgICkpKVxcblxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImNvaW4tY29udHJhY3QtdjVcIn0ifQ" ] diff --git a/src/Chainweb/Pact/Transactions/DevelopmentTransactions.hs b/src/Chainweb/Pact/Transactions/DevelopmentTransactions.hs index 18372b3d67..ca06167c8f 100644 --- a/src/Chainweb/Pact/Transactions/DevelopmentTransactions.hs +++ b/src/Chainweb/Pact/Transactions/DevelopmentTransactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.DevelopmentTransactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet0Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet0Transactions.hs index 24d29f8b07..6ba5186ede 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet0Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet0Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet0Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet1Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet1Transactions.hs index 4af6cc8fc5..09024cf524 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet1Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet1Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet1Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet2Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet2Transactions.hs index f22334b437..2ed4c34336 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet2Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet2Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet2Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet3Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet3Transactions.hs index 9f875b4b28..cce8a14b2c 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet3Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet3Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet3Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet4Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet4Transactions.hs index 6847076532..114ad275ff 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet4Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet4Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet4Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet5Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet5Transactions.hs index 6219f4a54b..c24ec780d1 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet5Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet5Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet5Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet6Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet6Transactions.hs index 0966bd938e..0183e4d61d 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet6Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet6Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet6Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet7Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet7Transactions.hs index 048e02771b..383a27cfde 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet7Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet7Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet7Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet8Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet8Transactions.hs index 39e32b8156..e18f1a3f43 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet8Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet8Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet8Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/Mainnet9Transactions.hs b/src/Chainweb/Pact/Transactions/Mainnet9Transactions.hs index 0fb9f8a44d..b4bb7b5cc4 100644 --- a/src/Chainweb/Pact/Transactions/Mainnet9Transactions.hs +++ b/src/Chainweb/Pact/Transactions/Mainnet9Transactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.Mainnet9Transactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/MainnetKADTransactions.hs b/src/Chainweb/Pact/Transactions/MainnetKADTransactions.hs index ffc0d56f74..4e25546216 100644 --- a/src/Chainweb/Pact/Transactions/MainnetKADTransactions.hs +++ b/src/Chainweb/Pact/Transactions/MainnetKADTransactions.hs @@ -5,14 +5,15 @@ module Chainweb.Pact.Transactions.MainnetKADTransactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoieThkd0Rkc0RZdmM5alg2WHZxVG1CSndEX2xlRlJUTWlJTXJMcjhKODlVOCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihpZlxcbiAgKDw9IDEwMC4wIChjb2luLmdldC1iYWxhbmNlIFxcXCJlN2Y3NjM0ZTkyNTU0MWYzNjhiODI3YWQ1YzcyNDIxOTA1MTAwZjYyMDUyODVhNzhjMTlkN2I0YTM4NzExODA1XFxcIikpXFxuXFxuICAoY29pbi5yZW1lZGlhdGUgXFxcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcXFwiIDEwMC4wKVxcbiAgXFxcIldhcm5pbmc6IGluc3VmZmljaWVudCBmdW5kcyBmb3IgcmVtZWRpYXRpb24sIHNvbGRpZXJpbmcgb25cXFwiKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibWFpbm5ldC1yZW1lZGlhdGlvbnMta2FkLW9wc1wifSJ9" ] diff --git a/src/Chainweb/Pact/Transactions/OtherTransactions.hs b/src/Chainweb/Pact/Transactions/OtherTransactions.hs index 6b5cebeaa2..1b33431b73 100644 --- a/src/Chainweb/Pact/Transactions/OtherTransactions.hs +++ b/src/Chainweb/Pact/Transactions/OtherTransactions.hs @@ -5,15 +5,16 @@ module Chainweb.Pact.Transactions.OtherTransactions ( transactions ) where import Data.Bifunctor (first) +import System.IO.Unsafe import Chainweb.Transaction import Chainweb.Utils -transactions :: IO [ChainwebTransaction] +transactions :: [ChainwebTransaction] transactions = let decodeTx t = - fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t - in mapM decodeTx [ + fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t + in unsafePerformIO $ mapM decodeTx [ "eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , "eyJoYXNoIjoibVZzMjNxNnJyUjZrWDFGX0ItamNCX05hLXdZdmR3dnRwa1cwQVNaZExjRSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyXCJ9In0" diff --git a/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs b/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs index 2064ff1e44..cdfc747741 100644 --- a/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs +++ b/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs @@ -30,7 +30,7 @@ import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 -upgradeTransactions :: ChainwebVersion -> ChainId -> IO [ChainwebTransaction] +upgradeTransactions :: ChainwebVersion dc -> ChainId -> IO [ChainwebTransaction] upgradeTransactions Mainnet01 cid = case cidInt of 0 -> MN0.transactions 1 -> MN1.transactions @@ -46,18 +46,18 @@ upgradeTransactions Mainnet01 cid = case cidInt of c -> internalError $ "Invalid mainnet chain id: " <> sshow c where cidInt :: Int cidInt = chainIdInt cid -upgradeTransactions Development cid = case chainIdInt @Int cid of +upgradeTransactions Development{} cid = case chainIdInt @Int cid of c | c >= 0, c <= 9 -> Devnet.transactions c | c >= 10, c <= 19 -> return [] c -> internalError $ "Invalid devnet chain id: " <> sshow c upgradeTransactions _ _ = Other.transactions -twentyChainUpgradeTransactions :: ChainwebVersion -> ChainId -> IO [ChainwebTransaction] +twentyChainUpgradeTransactions :: ChainwebVersion dc -> ChainId -> IO [ChainwebTransaction] twentyChainUpgradeTransactions Mainnet01 cid = case chainIdInt @Int cid of 0 -> MNKAD.transactions c | c >= 1, c <= 19 -> return [] c -> internalError $ "Invalid mainnet chain id: " <> sshow c -twentyChainUpgradeTransactions Development cid = case chainIdInt @Int cid of +twentyChainUpgradeTransactions Development{} cid = case chainIdInt @Int cid of 0 -> MNKAD.transactions -- just remeds c | c >= 1, c <= 19 -> return [] c -> internalError $ "Invalid devnet chain id: " <> sshow c diff --git a/src/Chainweb/Pact/Types.hs b/src/Chainweb/Pact/Types.hs index 1f4f60497a..072b1b5b86 100644 --- a/src/Chainweb/Pact/Types.hs +++ b/src/Chainweb/Pact/Types.hs @@ -11,6 +11,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE StrictData #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TypeFamilies #-} -- | -- Module: Chainweb.Pact.Types -- Copyright: Copyright © 2018 Kadena LLC. @@ -91,6 +92,7 @@ module Chainweb.Pact.Types , ctxToPublicData , ctxToPublicData' , ctxCurrentBlockHeight + , ctxChainId , ctxVersion , getTxContext @@ -181,6 +183,7 @@ import Chainweb.BlockHash import Chainweb.BlockHeader import Chainweb.BlockHeight import Chainweb.BlockHeaderDB +import Chainweb.ChainId import Chainweb.Mempool.Mempool (TransactionHash) import Chainweb.Miner.Pact import Chainweb.Logger @@ -192,6 +195,7 @@ import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Guards data Transactions r = Transactions @@ -466,7 +470,7 @@ updateInitCache mc = get >>= \PactServiceState{..} -> do psInitCache .= case M.lookupLE pbh _psInitCache of Nothing -> M.singleton pbh mc Just (_,before) - | chainweb217Pact After v pbh || chainweb217Pact At v pbh -> + | cleanModuleCache v (_chainId $ _psParentHeader) pbh -> M.insert pbh mc _psInitCache | otherwise -> M.insert pbh (HM.union mc before) _psInitCache @@ -516,6 +520,9 @@ ctxBlockHeader = _parentHeader . _tcParentHeader ctxCurrentBlockHeight :: TxContext -> BlockHeight ctxCurrentBlockHeight = succ . _blockHeight . ctxBlockHeader +ctxChainId :: TxContext -> ChainId +ctxChainId = _blockChainId . ctxBlockHeader + ctxVersion :: TxContext -> ChainwebVersion ctxVersion = _blockChainwebVersion . ctxBlockHeader diff --git a/src/Chainweb/Payload/PayloadStore.hs b/src/Chainweb/Payload/PayloadStore.hs index 2f02472786..0bbe5a0541 100644 --- a/src/Chainweb/Payload/PayloadStore.hs +++ b/src/Chainweb/Payload/PayloadStore.hs @@ -77,7 +77,7 @@ import GHC.Generics -- internal modules -import Chainweb.BlockHeader.Genesis (genesisBlockPayload) +import Chainweb.ChainId import Chainweb.Crypto.MerkleLog import Chainweb.MerkleUniverse import Chainweb.Payload @@ -127,10 +127,10 @@ type CanReadableTransactionDbCas_ a tbl = instance (pk ~ CasKeyType (PayloadData_ a), CanReadableTransactionDbCas_ a tbl) => ReadableTable (TransactionDb_ a tbl) pk (PayloadData_ a) where tableLookup db k = runMaybeT $ do - pd <- MaybeT $ tableLookup (_transactionDbBlockPayloads db) k + pd <- MaybeT $ tableLookup (_transactionDbBlockPayloads db) k let txsHash = _blockPayloadTransactionsHash pd let outsHash = _blockPayloadOutputsHash pd - txs <- MaybeT $ tableLookup (_transactionDbBlockTransactions db) txsHash + txs <- MaybeT $ tableLookup (_transactionDbBlockTransactions db) txsHash return $ PayloadData { _payloadDataTransactions = _blockTransactions txs , _payloadDataMiner = _blockMinerData txs @@ -222,7 +222,7 @@ initializePayloadDb initializePayloadDb v db = traverse_ initForChain $ chainIds v where initForChain cid = - addNewPayload db $ genesisBlockPayload v cid + addNewPayload db $ v ^?! versionGenesis . genesisBlockPayload . onChain cid -- -------------------------------------------------------------------------- -- -- Insert new Payload @@ -239,11 +239,11 @@ addPayload -> OutputTree_ a -> IO () addPayload db txs txTree outs outTree = do - casInsert (_transactionDbBlockPayloads $ _transactionDb db) payload - casInsert (_transactionDbBlockTransactions $ _transactionDb db) txs - casInsert (_payloadCacheBlockOutputs $ _payloadCache db) outs - casInsert (_payloadCacheTransactionTrees $ _payloadCache db) txTree - casInsert (_payloadCacheOutputTrees $ _payloadCache db) outTree + casInsert (_transactionDbBlockPayloads $ _transactionDb db) payload + casInsert (_transactionDbBlockTransactions $ _transactionDb db) txs + casInsert (_payloadCacheBlockOutputs $ _payloadCache db) outs + casInsert (_payloadCacheTransactionTrees $ _payloadCache db) txTree + casInsert (_payloadCacheOutputTrees $ _payloadCache db) outTree where payload = blockPayload txs outs @@ -303,7 +303,7 @@ instance (pk ~ CasKeyType (PayloadWithOutputs_ a), MerkleHashAlgorithm a, CanPay tableInsert db _ v = addNewPayload db v {-# INLINE tableInsert #-} - tableDelete db k = + tableDelete db k = tableLookup (_transactionDbBlockPayloads $ _transactionDb db) k >>= \case Just pd -> do tableDelete diff --git a/src/Chainweb/Payload/RestAPI/Server.hs b/src/Chainweb/Payload/RestAPI/Server.hs index 06d0349456..c3db888df7 100644 --- a/src/Chainweb/Payload/RestAPI/Server.hs +++ b/src/Chainweb/Payload/RestAPI/Server.hs @@ -141,7 +141,7 @@ outputsBatchHandler batchLimit db ks = liftIO -- Payload API Server payloadServer - :: forall tbl v c + :: forall tbl v c . CanReadablePayloadCas tbl => PayloadBatchLimit -> PayloadDb' tbl v c diff --git a/src/Chainweb/PowHash.hs b/src/Chainweb/PowHash.hs index 0c5ec5c389..6ac10d9c9f 100644 --- a/src/Chainweb/PowHash.hs +++ b/src/Chainweb/PowHash.hs @@ -28,7 +28,7 @@ module Chainweb.PowHash , powHashBytesCount , encodePowHash , decodePowHash -, powHash +, cryptoHash ) where import Control.DeepSeq @@ -61,7 +61,6 @@ import Chainweb.Crypto.MerkleLog import Chainweb.MerkleUniverse import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version -- -------------------------------------------------------------------------- -- -- PowHash @@ -128,15 +127,5 @@ instance FromJSON PowHash where -- -------------------------------------------------------------------------- -- -- Cryptographic Hash -powHash :: ChainwebVersion -> B.ByteString -> PowHash -powHash Test{} = cryptoHash @Blake2s_256 -powHash TimedConsensus{} = cryptoHash @Blake2s_256 -powHash PowConsensus{} = cryptoHash @Blake2s_256 -powHash TimedCPM{} = cryptoHash @Blake2s_256 -powHash FastTimedCPM{} = cryptoHash @Blake2s_256 -powHash Development = cryptoHash @Blake2s_256 -powHash Testnet04 = cryptoHash @Blake2s_256 -powHash Mainnet01 = cryptoHash @Blake2s_256 - cryptoHash :: forall a . HashAlgorithm a => B.ByteString -> PowHash cryptoHash = PowHash . SB.toShort . BA.convert . C.hash @_ @a diff --git a/src/Chainweb/RestAPI/NodeInfo.hs b/src/Chainweb/RestAPI/NodeInfo.hs index 4de732a2b9..af5c7f1a2e 100644 --- a/src/Chainweb/RestAPI/NodeInfo.hs +++ b/src/Chainweb/RestAPI/NodeInfo.hs @@ -28,10 +28,12 @@ import GHC.Generics import Servant import Chainweb.BlockHeight +import Chainweb.ChainId import Chainweb.Cut.CutHashes import Chainweb.CutDB import Chainweb.Graph import Chainweb.RestAPI.Utils +import Chainweb.Utils.Rule import Chainweb.Version type NodeInfoApi = "info" :> Get '[JSON] NodeInfo @@ -45,7 +47,7 @@ someNodeInfoServer v c = data NodeInfo = NodeInfo { - nodeVersion :: ChainwebVersion + nodeVersion :: ChainwebVersionName , nodeApiVersion :: Text , nodeChains :: [Text] -- ^ Current list of chains @@ -54,6 +56,8 @@ data NodeInfo = NodeInfo , nodeGraphHistory :: [(BlockHeight, [(Int, [Int])])] -- ^ List of chain graphs and the block height they took effect. Sorted -- descending by height so the current chain graph is at the beginning. + , nodeLatestBehaviorHeight :: BlockHeight + -- ^ edtodo document } deriving (Show, Eq, Generic) instance ToJSON NodeInfo @@ -68,11 +72,12 @@ nodeInfoHandler v (SomeCutDb ((CutDbT db) :: CutDbT cas v)) = do curGraph = head $ dropWhile (\(h,_) -> h > curHeight) graphs curChains = map fst $ snd curGraph return $ NodeInfo - { nodeVersion = v + { nodeVersion = _versionName v , nodeApiVersion = prettyApiVersion , nodeChains = T.pack . show <$> curChains , nodeNumberOfChains = length curChains , nodeGraphHistory = graphs + , nodeLatestBehaviorHeight = latestBehaviorAt v } -- | Converts chainwebGraphs to a simpler structure that has invertible JSON @@ -80,7 +85,7 @@ nodeInfoHandler v (SomeCutDb ((CutDbT db) :: CutDbT cas v)) = do unpackGraphs :: ChainwebVersion -> [(BlockHeight, [(Int, [Int])])] unpackGraphs v = gs where - gs = map (second graphAdjacencies) $ NE.toList $ chainwebGraphs v + gs = map (second graphAdjacencies) $ NE.toList $ ruleElems (BlockHeight 0) $ _versionGraphs v graphAdjacencies = map unChain . HashMap.toList . fmap HashSet.toList . G.adjacencySets . view chainGraphGraph unChain (a, bs) = (chainIdInt a, map chainIdInt bs) diff --git a/src/Chainweb/RestAPI/Orphans.hs b/src/Chainweb/RestAPI/Orphans.hs index 388b07b8a4..3aa7ed2a64 100644 --- a/src/Chainweb/RestAPI/Orphans.hs +++ b/src/Chainweb/RestAPI/Orphans.hs @@ -92,10 +92,10 @@ instance FromHttpApiData BlockPayloadHash where instance ToHttpApiData BlockPayloadHash where toUrlPiece = encodeB64UrlNoPaddingText . runPutS . encodeBlockPayloadHash -instance FromHttpApiData ChainwebVersion where +instance FromHttpApiData ChainwebVersionName where parseUrlPiece = first T.pack . eitherFromText -instance ToHttpApiData ChainwebVersion where +instance ToHttpApiData ChainwebVersionName where toUrlPiece = toText instance FromHttpApiData ChainId where diff --git a/src/Chainweb/Rosetta/Internal.hs b/src/Chainweb/Rosetta/Internal.hs index 1219244682..9919a3346b 100644 --- a/src/Chainweb/Rosetta/Internal.hs +++ b/src/Chainweb/Rosetta/Internal.hs @@ -15,7 +15,7 @@ module Chainweb.Rosetta.Internal where import Control.Error.Util -import Control.Lens ((^?)) +import Control.Lens hiding ((??), from, to) import Control.Monad (foldM) import Control.Monad.Except (throwError) import Control.Monad.IO.Class @@ -52,10 +52,11 @@ import Servant.Server -- internal modules import Chainweb.BlockHash -import Chainweb.BlockHeader (BlockHeader(..)) +import Chainweb.BlockHeader +import Chainweb.ChainId import Chainweb.Cut import Chainweb.CutDB -import Chainweb.Pact.Transactions.UpgradeTransactions +-- import Chainweb.Pact.Transactions.UpgradeTransactions import Chainweb.Pact.Service.Types (Domain'(..), BlockTxHistory(..)) import Chainweb.Payload hiding (Transaction(..)) import Chainweb.Payload.PayloadStore @@ -120,11 +121,7 @@ matchLogs -> ExceptT RosettaFailure Handler tx matchLogs typ bh logs coinbase txs | bheight == genesisHeight v cid = matchGenesis - | coinV2Upgrade v cid bheight = matchRemediation (upgradeTransactions v cid) - | to20ChainRebalance v cid bheight = matchRemediation (twentyChainUpgradeTransactions v cid) - | pact4coin3Upgrade At v bheight = matchRemediation coinV3Transactions - | chainweb214Pact At v bheight = matchRemediation coinV4Transactions - | chainweb215Pact At v bheight = matchRemediation coinV5Transactions + | Just upg <- v ^? versionUpgrades . onChain cid . at bheight . _Just = matchRemediation upg | otherwise = matchRest where bheight = _blockHeight bh @@ -135,16 +132,15 @@ matchLogs typ bh logs coinbase txs FullLogs -> genesisTransactions logs cid txs SingleLog rk -> genesisTransaction logs cid txs rk - matchRemediation getRemTxs = do - rems <- liftIO getRemTxs + matchRemediation upg = do hoistEither $ case typ of FullLogs -> overwriteError RosettaMismatchTxLogs $! - remediations logs cid coinbase rems txs + remediations logs cid coinbase (_upgradeTransactions upg) txs SingleLog rk -> (noteOptional RosettaTxIdNotFound . overwriteError RosettaMismatchTxLogs) $ - singleRemediation logs cid coinbase rems txs rk + singleRemediation logs cid coinbase (_upgradeTransactions upg) txs rk matchRest = hoistEither $ case typ of FullLogs -> @@ -747,7 +743,7 @@ toSignerAcctsMap txInfo payerAcct cid pacts cutDb = do someActualFrom <- getOwnership peCurr bhCurr from someActualTo <- getOwnership peCurr bhCurr to - _ <- enforceAcctPresent from someActualFrom + _ <- enforceAcctPresent' from someActualFrom checkExpectedOwnership from expectedFrom someActualFrom checkExpectedOwnership to expectedTo someActualTo @@ -821,6 +817,18 @@ enforceAcctPresent k actualOwnership = stringRosettaError RosettaInvalidAccountProvided $ "Account=" ++ show k ++ " doesn't exists" +enforceAcctPresent' + :: AccountId + -> Maybe [T.Text] + -> ExceptT RosettaError Handler [T.Text] +enforceAcctPresent' k actualOwnership = + case actualOwnership of + Just pks -> pure pks + Nothing -> -- key missing (not expected) + hoistEither $ Left $ + stringRosettaError RosettaInvalidAccountProvided $ + "Account=" ++ show k ++ " doesn't exists (2)" + checkExpectedOwnership :: AccountId -> [T.Text] diff --git a/src/Chainweb/Rosetta/RestAPI.hs b/src/Chainweb/Rosetta/RestAPI.hs index d324f20897..f07c7de79a 100644 --- a/src/Chainweb/Rosetta/RestAPI.hs +++ b/src/Chainweb/Rosetta/RestAPI.hs @@ -56,7 +56,7 @@ module Chainweb.Rosetta.RestAPI ) where import Control.Error.Util -import Control.Monad (when) +import Control.Monad import Data.Aeson (encode) @@ -66,9 +66,9 @@ import Servant -- internal modules +import Chainweb.ChainId import Chainweb.Rosetta.Utils import Chainweb.RestAPI.Utils (ChainwebEndpoint(..), Reassoc) -import Chainweb.Utils import Chainweb.Version --- @@ -322,6 +322,6 @@ throwRosettaError e = throwError err500 { errBody = encode e } validateNetwork :: ChainwebVersion -> NetworkId -> Either RosettaFailure ChainId validateNetwork v (NetworkId bc n msni) = do when (bc /= "kadena") $ Left RosettaInvalidBlockchainName - when (Just v /= fromText n) $ Left RosettaMismatchNetworkName + when (_versionName v /= ChainwebVersionName n) $ Left RosettaMismatchNetworkName SubNetworkId cid _ <- note RosettaChainUnspecified msni note RosettaInvalidChain $ readChainIdText v cid diff --git a/src/Chainweb/Rosetta/RestAPI/Client.hs b/src/Chainweb/Rosetta/RestAPI/Client.hs index 7117c44b99..8da337d0ac 100644 --- a/src/Chainweb/Rosetta/RestAPI/Client.hs +++ b/src/Chainweb/Rosetta/RestAPI/Client.hs @@ -22,7 +22,7 @@ module Chainweb.Rosetta.RestAPI.Client , rosettaBlockApiClient -- * Construction Endpoints , rosettaConstructionDeriveApiClient -, rosettaConstructionPreprocessApiClient +, rosettaConstructionPreprocessApiClient , rosettaConstructionMetadataApiClient , rosettaConstructionPayloadsApiClient , rosettaConstructionParseApiClient diff --git a/src/Chainweb/Rosetta/RestAPI/Server.hs b/src/Chainweb/Rosetta/RestAPI/Server.hs index 39ff040282..0d28ac8b96 100644 --- a/src/Chainweb/Rosetta/RestAPI/Server.hs +++ b/src/Chainweb/Rosetta/RestAPI/Server.hs @@ -6,6 +6,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE ViewPatterns #-} -- | -- Module: Chainweb.Rosetta.RestAPI.Server @@ -43,8 +44,8 @@ import Servant.Server -- internal modules -import Chainweb.BlockHeader (BlockHeader(..)) -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) +import Chainweb.BlockHeader +import Chainweb.ChainId import Chainweb.Cut (_cutMap) import Chainweb.CutDB import Chainweb.HostAddress @@ -246,7 +247,7 @@ constructionPreprocessH v req = do either throwRosettaError pure work where ConstructionPreprocessReq net ops someMeta someMaxFee someMult = req - + work :: Either RosettaError ConstructionPreprocessResp work = do _ <- annotate rosettaError' (validateNetwork v net) @@ -286,7 +287,7 @@ constructionMetadataH constructionMetadataH v cutDb pacts (ConstructionMetadataReq net opts someKeys) = runExceptT work >>= either throwRosettaError pure where - + work :: ExceptT RosettaError Handler ConstructionMetadataResp work = do cid <- hoistEither $ annotate rosettaError' (validateNetwork v net) @@ -302,7 +303,7 @@ constructionMetadataH v cutDb pacts (ConstructionMetadataReq net opts someKeys) expectedAccts <- toSignerAcctsMap tx payer cid pacts cutDb signersAndAccts <- hoistEither $! createSigners availableSigners expectedAccts - + pure $! ConstructionMetadataResp { _constructionMetadataResp_metadata = toObject $! PayloadsMetaData { _payloadsMetaData_signers = signersAndAccts @@ -425,7 +426,7 @@ constructionSubmitH v ms (ConstructionSubmitReq net tx) = $ "Validation failed for hash " ++ (show $! hsh) ++ ": " ++ show insErr - + work :: ExceptT RosettaError Handler TransactionIdResp work = do cid <- hoistEither $ annotate rosettaError' (validateNetwork v net) @@ -520,7 +521,7 @@ networkListH v cutDb _ = runExceptT work >>= either throwRosetta pure f :: ChainId -> NetworkId f cid = NetworkId { _networkId_blockchain = "kadena" - , _networkId_network = chainwebVersionToText v + , _networkId_network = getChainwebVersionName $ _versionName v , _networkId_subNetworkId = Just (SubNetworkId (chainIdToText cid) Nothing) } @@ -541,7 +542,7 @@ networkOptionsH v (NetworkReq nid _) = runExceptT work >>= either throwRosetta p -- TODO: Document this meta data metaPairs = [ "node-api-version" .= prettyApiVersion - , "chainweb-version" .= chainwebVersionToText v + , "chainweb-version" .= getChainwebVersionName (_versionName v) , "rosetta-chainweb-version" .= rosettaImplementationVersion -- The version of the rosetta implementation. -- Meant to capture if something about the internal diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index c3c3d29790..7a5f1a1b72 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -20,7 +20,7 @@ import Data.Foldable (foldl') import Data.Decimal ( Decimal, DecimalRaw(Decimal) ) import Data.Hashable (Hashable(..)) import Data.List (sortOn, inits) -import Data.Word (Word64) +import Data.Word (Word32, Word64) import Text.Read (readMaybe) import Text.Printf ( printf ) @@ -53,8 +53,9 @@ import Rosetta import Chainweb.BlockCreationTime (BlockCreationTime(..)) import Chainweb.BlockHash ( blockHashToText ) -import Chainweb.BlockHeader (BlockHeader(..)) +import Chainweb.BlockHeader import Chainweb.BlockHeight (BlockHeight(..)) +import Chainweb.ChainId import Chainweb.Pact.Utils import Chainweb.Time import Chainweb.Utils ( sshow, int, T2(..) ) @@ -433,7 +434,7 @@ getSuggestedFee tx someMaxFees someMult = do -- - https://explorer.chainweb.com/mainnet/txdetail/EUiZfeHHeisKMP2uHpzyAcMOIqZJVsJB6sT_ABpBUsQ -- - https://explorer.chainweb.com/mainnet/txdetail/-cb0Pz6rKb1NVhAFQ_Bcz2V2dGPjTmIiVBl-gXMLGRQ -- - https://explorer.chainweb.com/mainnet/txdetail/2riuW2nBmbN2dzmyAh5b2lUns5SPARb44-QN_EKzzmk - defGasUnitsTransferCreate = 1000 + defGasUnitsTransferCreate = 4000 -- See Chainweb.Chainweb.Configuration for latest min gas minGasPrice = Decimal 8 1 @@ -700,7 +701,7 @@ createUnsignedCmd v meta = do PayloadsMetaData signers nonce pubMeta txInfo = meta signerAccts = map snd signers pactSigners = map fst signers - networkId = Just $ P.NetworkId $! chainwebVersionToText v + networkId = Just $! P.NetworkId $! getChainwebVersionName $ _versionName v pactRPC = constructionTxToPactRPC txInfo @@ -852,13 +853,13 @@ parentBlockId bh cid = _blockChainId bh v = _blockChainwebVersion bh parent = BlockId - { _blockId_index = _height (pred $ _blockHeight bh) + { _blockId_index = getBlockHeight (pred $ _blockHeight bh) , _blockId_hash = blockHashToText (_blockParent bh) } blockId :: BlockHeader -> BlockId blockId bh = BlockId - { _blockId_index = _height (_blockHeight bh) + { _blockId_index = getBlockHeight (_blockHeight bh) , _blockId_hash = blockHashToText (_blockHash bh) } @@ -1204,7 +1205,7 @@ extractMetaData = toRosettaError RosettaUnparsableMetaData -- readChainIdText :: ChainwebVersion -> T.Text -> Maybe ChainId readChainIdText v c = do - cid <- readMaybe @Word (T.unpack c) + cid <- readMaybe @Word32 (T.unpack c) mkChainId v maxBound cid -- TODO: document diff --git a/src/Chainweb/Sync/WebBlockHeaderStore.hs b/src/Chainweb/Sync/WebBlockHeaderStore.hs index fa597d9385..cfd7acba7e 100644 --- a/src/Chainweb/Sync/WebBlockHeaderStore.hs +++ b/src/Chainweb/Sync/WebBlockHeaderStore.hs @@ -59,7 +59,6 @@ import System.LogLevel import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeader.Validation import Chainweb.BlockHeaderDB import Chainweb.ChainId @@ -174,9 +173,9 @@ memoInsert cas m k a = tableLookup cas k >>= \case -- getBlockPayload :: CanReadablePayloadCas tbl - => Cas candidateCas PayloadData + => Cas candidateCas PayloadData => WebBlockPayloadStore tbl - -> candidateCas + -> candidateCas -> Priority -> Maybe PeerInfo -- ^ Peer from with the BlockPayloadHash originated, if available. @@ -265,8 +264,8 @@ getBlockHeaderInternal => PayloadDataCas candidatePayloadCas => WebBlockHeaderStore -> WebBlockPayloadStore tbl - -> candidateHeaderCas - -> candidatePayloadCas + -> candidateHeaderCas + -> candidatePayloadCas -> Priority -> Maybe PeerInfo -> ChainValue BlockHash @@ -440,7 +439,7 @@ getBlockHeaderInternal headerStore payloadStore candidateHeaderCas candidatePayl (_blockHash hdr) (length (_payloadDataTransactions p)) $ pact hdr p - casInsert (_webBlockPayloadStoreCas payloadStore) outs + casInsert (_webBlockPayloadStoreCas payloadStore) outs queryBlockHeaderTask ck@(ChainValue cid k) = newTask (sshow ck) priority $ \l env -> chainValue <$> do @@ -524,7 +523,7 @@ getBlockHeader => WebBlockHeaderStore -> WebBlockPayloadStore tbl -> candidateHeaderCas - -> candidatePayloadCas + -> candidatePayloadCas -> ChainId -> Priority -> Maybe PeerInfo @@ -553,7 +552,7 @@ instance (CasKeyType (ChainValue BlockHeader) ~ k) => ReadableTable WebBlockHead {-# INLINE tableLookup #-} instance (CasKeyType (ChainValue BlockHeader) ~ k) => Table WebBlockHeaderCas k (ChainValue BlockHeader) where - tableInsert (WebBlockHeaderCas db) _ (ChainValue _ h) + tableInsert (WebBlockHeaderCas db) _ (ChainValue _ h) = insertWebBlockHeaderDb db h {-# INLINE tableInsert #-} diff --git a/src/Chainweb/Transaction.hs b/src/Chainweb/Transaction.hs index 28285637c7..e9779fae6f 100644 --- a/src/Chainweb/Transaction.hs +++ b/src/Chainweb/Transaction.hs @@ -11,6 +11,7 @@ module Chainweb.Transaction ( ChainwebTransaction , HashableTrans(..) , PayloadWithText + , PactParserVersion(..) , chainwebPayloadCodec , encodePayload , decodePayload @@ -47,8 +48,6 @@ import Pact.Types.Hash import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version -import Chainweb.BlockHeight -- | A product type representing a `Payload PublicMeta ParsedCode` coupled with -- the Text that generated it, to make gossiping easier. @@ -60,14 +59,12 @@ data PayloadWithText = PayloadWithText deriving (Show, Eq, Generic) deriving anyclass (NFData) - payloadBytes :: PayloadWithText -> SB.ShortByteString payloadBytes = _payloadBytes payloadObj :: PayloadWithText -> Payload PublicMeta ParsedCode payloadObj = _payloadObj - mkPayloadWithText :: Command ByteString -> Payload PublicMeta ParsedCode -> PayloadWithText mkPayloadWithText cmd p = PayloadWithText { _payloadBytes = SB.toShort $ _cmdPayload cmd @@ -80,9 +77,13 @@ mkPayloadWithTextOld p = PayloadWithText , _payloadObj = p } - type ChainwebTransaction = Command PayloadWithText +data PactParserVersion + = PactParserGenesis + | PactParserChainweb213 + deriving (Eq, Ord, Bounded, Show, Enum) + -- | Hashable newtype of ChainwebTransaction newtype HashableTrans a = HashableTrans { unHashable :: Command a } deriving (Eq, Functor, Ord) @@ -97,37 +98,35 @@ instance Hashable (HashableTrans PayloadWithText) where -- | A codec for (Command PayloadWithText) transactions. chainwebPayloadCodec - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> Codec (Command PayloadWithText) -chainwebPayloadCodec chainCtx = Codec enc dec +chainwebPayloadCodec ppv = Codec enc dec where enc c = encodeToByteString $ fmap (decodeUtf8 . encodePayload) c dec bs = case Aeson.decodeStrict' bs of - Just cmd -> traverse (decodePayload chainCtx . encodeUtf8) cmd + Just cmd -> traverse (decodePayload ppv . encodeUtf8) cmd Nothing -> Left "decode PayloadWithText failed" encodePayload :: PayloadWithText -> ByteString encodePayload = SB.fromShort . _payloadBytes decodePayload - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -> ByteString -> Either String PayloadWithText -decodePayload chainCtx bs = case Aeson.decodeStrict' bs of +decodePayload ppv bs = case Aeson.decodeStrict' bs of Just payload -> do - p <- traverse (parsePact chainCtx) payload + p <- traverse (parsePact ppv) payload return $! PayloadWithText (SB.toShort bs) p Nothing -> Left "decoding Payload failed" parsePact - :: Maybe (ChainwebVersion, BlockHeight) + :: PactParserVersion -- ^ If the chain context is @Nothing@, latest parser version is used. -> Text -> Either String ParsedCode -parsePact Nothing code = P.parsePact code -parsePact (Just (v, h)) code - | chainweb213Pact v h = P.parsePact code - | otherwise = P.legacyParsePact code +parsePact PactParserChainweb213 = P.parsePact +parsePact PactParserGenesis = P.legacyParsePact -- | Access the gas limit/supply of a public chain command payload cmdGasLimit :: Lens' (Command (Payload PublicMeta c)) GasLimit diff --git a/src/Chainweb/TreeDB/RemoteDB.hs b/src/Chainweb/TreeDB/RemoteDB.hs index 50dd525f0a..6890eef953 100644 --- a/src/Chainweb/TreeDB/RemoteDB.hs +++ b/src/Chainweb/TreeDB/RemoteDB.hs @@ -39,11 +39,10 @@ import System.LogLevel import Chainweb.BlockHash (BlockHash) import Chainweb.BlockHeader (BlockHeader(..)) import Chainweb.BlockHeaderDB.RestAPI.Client -import Chainweb.ChainId (ChainId) import Chainweb.TreeDB import Chainweb.Utils import Chainweb.Utils.Paging -import Chainweb.Version (ChainwebVersion) +import Chainweb.Version import Data.LogMessage diff --git a/src/Chainweb/Utils.hs b/src/Chainweb/Utils.hs index 769a2d8842..28d3236b35 100644 --- a/src/Chainweb/Utils.hs +++ b/src/Chainweb/Utils.hs @@ -191,6 +191,7 @@ module Chainweb.Utils , scurry , suncurry , suncurry3 +, uncurry3 , rwipe3 , _T2 , _T3 @@ -1279,6 +1280,10 @@ suncurry :: (a -> b -> c) -> T2 a b -> c suncurry k (T2 a b) = k a b {-# INLINE suncurry #-} +uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d +uncurry3 k (a, b, c) = k a b c +{-# INLINE uncurry3 #-} + suncurry3 :: (a -> b -> c -> d) -> T3 a b c -> d suncurry3 k (T3 a b c) = k a b c {-# INLINE suncurry3 #-} diff --git a/src/Chainweb/Utils/Rule.hs b/src/Chainweb/Utils/Rule.hs new file mode 100644 index 0000000000..9b0d24321f --- /dev/null +++ b/src/Chainweb/Utils/Rule.hs @@ -0,0 +1,94 @@ +{-# language DeriveAnyClass #-} +{-# language DeriveGeneric #-} +{-# language DeriveFoldable #-} +{-# language DeriveFunctor #-} +{-# language DeriveTraversable #-} +{-# language DerivingStrategies #-} +{-# language TupleSections #-} + +-- edTODO document + +module Chainweb.Utils.Rule where + +import Control.DeepSeq + +import Data.Aeson +import Data.Hashable +import qualified Data.List.NonEmpty as NE +import Data.Functor.Apply +import Data.Semigroup.Foldable +import Data.Semigroup.Traversable +import qualified Data.Vector as V + +import GHC.Generics + +data Rule h a = Above (h, a) (Rule h a) | End a + deriving stock (Eq, Ord, Show, Foldable, Functor, Generic, Generic1, Traversable) + deriving anyclass (Hashable, NFData) + +instance Foldable1 (Rule h) where foldMap1 = foldMap1Default +instance Traversable1 (Rule h) where + traverse1 f (Above (h, a) t) = Above <$> ((h,) <$> f a) <.> traverse1 f t + traverse1 f (End a) = End <$> f a + +instance (ToJSON h, ToJSON a) => ToJSON (Rule h a) where + toJSON = toJSON . go + where + go (Above (h, a) t) = toJSON (toJSON h, toJSON a) : go t + go (End a) = [toJSON a] + +instance (FromJSON h, FromJSON a) => FromJSON (Rule h a) where + parseJSON = withArray "Rule" $ go . V.toList + where + go [] = fail "empty list" + go [a] = End <$> parseJSON a + go (x:xs) = Above <$> parseJSON x <*> go xs + +ruleHead :: Rule h a -> (Maybe h, a) +ruleHead (Above (h, a) _) = (Just h, a) +ruleHead (End a) = (Nothing, a) + +ruleTakeWhile :: (h -> Bool) -> Rule h a -> Rule h a +ruleTakeWhile p (Above (h, a) t) + | p h = Above (h, a) (ruleTakeWhile p t) + | otherwise = ruleTakeWhile p t +ruleTakeWhile _ t = t + +ruleDropWhile :: (h -> Bool) -> Rule h a -> Rule h a +ruleDropWhile p (Above (h, a) t) + | p h = ruleDropWhile p t + | otherwise = Above (h, a) t +ruleDropWhile _ t = t + +data Measurement h a = Bottom a | Top (h, a) | Between (h, a) (h, a) + +measureRule' :: (h -> Bool) -> Rule h a -> Measurement h a +measureRule' p ((topH, topA) `Above` topTail) + | p topH = Top (topH, topA) + | otherwise = go topH topA topTail + where + go lh la (Above (h, a) t) + | p h = Between (lh, la) (h, a) + | otherwise = go h a t + go _ _ (End a) = Bottom a +measureRule' _ (End a) = Bottom a + +measureRule :: Ord h => h -> Rule h a -> Measurement h a +measureRule h = + measureRule' (\hc -> h >= hc) + +ruleElemHeight :: Eq a => (a -> Bool) -> Rule h a -> Maybe (Maybe h) +ruleElemHeight p (Above (h, a) t) + | p a = Just (Just h) + | otherwise = ruleElemHeight p t +ruleElemHeight p (End a) + | p a = Just Nothing + | otherwise = Nothing + +ruleElems :: h -> Rule h a -> NE.NonEmpty (h, a) +ruleElems h (End a) = (h, a) NE.:| [] +ruleElems he (Above (h, a) t) = (h, a) `NE.cons` ruleElems he t + +ruleValid :: Ord h => Rule h a -> Bool +ruleValid (Above (h, _) t@(Above (h', _) _)) = h >= h' && ruleValid t +ruleValid _ = True \ No newline at end of file diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 7767771dca..53b33f9150 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -1,16 +1,28 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} @@ -25,408 +37,453 @@ -- Properties of Chainweb Versions -- module Chainweb.Version -( ChainwebVersion(..) -, encodeChainwebVersion -, decodeChainwebVersion -, chainwebVersionFromText -, chainwebVersionToText -, chainwebVersionId - --- * Properties of Chainweb Version --- ** Chain Graph -, chainwebGraphs -, genesisGraph -, genesisHeight -, to20ChainsDevelopment --- ** POW -, BlockRate(..) -, blockRate -, WindowWidth(..) -, window -, headerSizeBytes -, workSizeBytes --- ** Payload Validation Parameters -, maxBlockGasLimit --- ** Payload Validation Guards -, vuln797Fix -, coinV2Upgrade -, to20ChainRebalance -, pactBackCompat_v16 -, skipTxTimingValidation -, enableModuleNameFix -, enableModuleNameFix2 -, enablePactEvents -, enableSPVBridge -, pact4coin3Upgrade -, pact420Upgrade -, enforceKeysetFormats -, AtOrAfter(..) -, doCheckTxHash -, chainweb213Pact -, chainweb214Pact -, chainweb215Pact -, chainweb216Pact -, chainweb217Pact -, pact44NewTrans - --- ** BlockHeader Validation Guards -, slowEpochGuard -, oldTargetGuard -, skipFeatureFlagValidationGuard -, oldDaGuard - --- * Typelevel ChainwebVersion -, ChainwebVersionT(..) -, ChainwebVersionSymbol -, chainwebVersionSymbolVal -, SomeChainwebVersionT(..) -, KnownChainwebVersionSymbol -, someChainwebVersionVal - --- * Singletons -, Sing(SChainwebVersion) -, SChainwebVersion -, pattern FromSingChainwebVersion - --- * HasChainwebVersion -, HasChainwebVersion(..) -, mkChainId -, chainIds - --- * ChainId -, module Chainweb.ChainId - --- * Re-exports from Chainweb.ChainGraph - --- ** Chain Graph -, ChainGraph -, HasChainGraph(..) -, adjacentChainIds -, chainGraphAt -, chainGraphAt_ -, chainwebGraphsAt - --- ** Graph Properties -, order -, diameter -, degree -, shortestPath - --- ** Undirected Edges -, AdjPair -, _getAdjPair -, pattern Adj -, adjs -, adjsOfVertex -, checkAdjacentChainIds - --- * Internal. Don't use. Exported only for testing -, headerSizes -, headerBaseSizeBytes -) where + ( Fork(..) + , ChainwebGenesis(..) + , Cheats(..) + , disableMempool + , disablePact + , disablePeerValidation + , disablePow + , ChainwebVersionCode(..) + , ChainwebVersionName(..) + , ChainwebVersion(..) + , Upgrade(..) + , upgrade + , versionForks + , versionBlockRate + , versionCheats + , versionUpgrades + , versionBootstraps + , versionCode + , versionFakeFirstEpochStart + , versionGraphs + , versionHeaderBaseSizeBytes + , versionMaxBlockGasLimit + , versionName + , versionWindow + , window + , blockRate + , versionGenesis + , genesisBlockPayload + , genesisBlockPayloadHash + , genesisBlockTarget + , genesisTime + , emptyPayload + , forkUpgrades + , latestBehaviorAt + , domainAddr2PeerInfo + , encodeChainwebVersionCode + , decodeChainwebVersionCode + -- , chainwebVersionFromText + -- , chainwebVersionToText + -- , chainwebVersionId + + -- * Properties of Chainweb Version + -- ** POW + , BlockRate(..) + , WindowWidth(..) + -- ** Payload Validation Parameters + , maxBlockGasLimit + + -- * Typelevel ChainwebVersion + , ChainwebVersionT(..) + , ChainwebVersionSymbol + , chainwebVersionSymbolVal + , SomeChainwebVersionT(..) + , KnownChainwebVersionSymbol + , someChainwebVersionVal + + -- * Singletons + , Sing(SChainwebVersion) + , SChainwebVersion + , pattern FromSingChainwebVersion + + -- * HasChainwebVersion + , HasChainwebVersion(..) + , mkChainId + , chainIds + + -- * ChainId + , module Chainweb.ChainId + + -- * Re-exports from Chainweb.ChainGraph + + -- ** Chain Graph + , ChainGraph + , HasChainGraph(..) + , adjacentChainIds + , chainGraphAt + , chainGraphAt_ + , chainwebGraphsAt + + -- ** Graph Properties + , order + , diameter + , degree + , shortestPath + + -- ** Undirected Edges + , AdjPair + , _getAdjPair + , pattern Adj + , adjs + , adjsOfVertex + , checkAdjacentChainIds + + -- * Internal. Don't use. Exported only for testing + -- , headerSizes + -- , headerBaseSizeBytes + ) where import Control.DeepSeq -import Control.Lens -import Control.Monad +import Control.Lens hiding ((.=), (<.>), index) import Control.Monad.Catch import Data.Aeson hiding (pairs) -import Data.Bits +import Data.Aeson.Types +import Data.Foldable import Data.Hashable +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS -import qualified Data.List.NonEmpty as NE import Data.Proxy import qualified Data.Text as T import Data.Word -import GHC.Generics (Generic) +import GHC.Generics(Generic) import GHC.Stack import GHC.TypeLits import Numeric.Natural -import System.IO.Unsafe (unsafeDupablePerformIO) -import System.Environment (lookupEnv) - -import Text.Read (readMaybe) - -- internal modules +import Chainweb.BlockCreationTime import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Crypto.MerkleLog +import Chainweb.Difficulty import Chainweb.Graph +import Chainweb.HostAddress import Chainweb.MerkleUniverse -import Chainweb.Time +import Chainweb.Miner.Pact +import Chainweb.Payload +import Chainweb.Transaction import Chainweb.Utils +import Chainweb.Utils.Rule import Chainweb.Utils.Serialization import Data.Singletons +import P2P.Peer + -- -------------------------------------------------------------------------- -- --- Chainweb Version +-- Bootstrap Peer Info + +-- | Official testnet bootstrap nodes +-- +domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] +domainAddr2PeerInfo = fmap (PeerInfo Nothing) + +data Fork + = Vuln797Fix + | SlowEpoch + | OldTargetGuard + | EnforceKeysetFormats + | SkipFeatureFlagValidation + | OldDAGuard + | CheckTxHash + | PactEvents + | SkipTxTimingValidation + | SPVBridge + | ModuleNameFix + | ModuleNameFix2 + | PactBackCompat_v16 + | CoinV2 + | Pact4Coin3 + | Pact420 + | Chainweb213Pact + | Chainweb214Pact + | Chainweb215Pact + | Chainweb216Pact + | Chainweb217Pact + | Pact44NewTrans + -- always add new forks at the end, not in the middle of the constructors. + deriving (Bounded, Generic, NFData, Hashable, Eq, Enum, Ord, Show) + +instance HasTextRepresentation Fork where + toText Vuln797Fix = "vuln797Fix" + toText CoinV2 = "coinV2" + toText SlowEpoch = "slowEpoch" + toText OldTargetGuard = "oldTargetGuard" + toText EnforceKeysetFormats = "enforceKeysetFormats" + toText SkipFeatureFlagValidation = "skipFeatureFlagValidation" + toText OldDAGuard = "oldDaGuard" + toText CheckTxHash = "checkTxHash" + toText Pact4Coin3 = "pact4Coin3" + toText PactEvents = "pactEvents" + toText SkipTxTimingValidation = "skipTxTimingValidation" + toText SPVBridge = "spvBridge" + toText PactBackCompat_v16 = "pactBackCompat_v16" + toText ModuleNameFix = "moduleNameFix" + toText ModuleNameFix2 = "moduleNameFix2" + toText Pact420 = "pact420" + toText Chainweb213Pact = "chainweb213Pact" + toText Chainweb214Pact = "chainweb214Pact" + toText Chainweb215Pact = "chainweb215Pact" + toText Chainweb216Pact = "chainweb216Pact" + toText Chainweb217Pact = "chainweb217Pact" + toText Pact44NewTrans = "pact44NewTrans" + + fromText "vuln797Fix" = return Vuln797Fix + fromText "coinV2" = return CoinV2 + fromText "slowEpoch" = return SlowEpoch + fromText "oldTargetGuard" = return OldTargetGuard + fromText "enforceKeysetFormats" = return EnforceKeysetFormats + fromText "skipFeatureFlagValidation" = return SkipFeatureFlagValidation + fromText "oldDaGuard" = return OldDAGuard + fromText "checkTxHash" = return CheckTxHash + fromText "pact4Coin3" = return Pact4Coin3 + fromText "pactEvents" = return PactEvents + fromText "skipTxTimingValidation" = return SkipTxTimingValidation + fromText "spvBridge" = return SPVBridge + fromText "pactBackCompat_v16" = return PactBackCompat_v16 + fromText "moduleNameFix" = return ModuleNameFix + fromText "moduleNameFix2" = return ModuleNameFix2 + fromText "pact420" = return Pact420 + fromText "chainweb213Pact" = return Chainweb213Pact + fromText "chainweb214Pact" = return Chainweb214Pact + fromText "chainweb215Pact" = return Chainweb215Pact + fromText "chainweb216Pact" = return Chainweb216Pact + fromText "chainweb217Pact" = return Chainweb217Pact + fromText "pact44NewTrans" = return Pact44NewTrans + fromText t = throwM . TextFormatException $ "Unknown Chainweb fork: " <> t + +instance ToJSON Fork where + toJSON = toJSON . toText +instance ToJSONKey Fork where + toJSONKey = toJSONKeyText toText +instance FromJSON Fork where + parseJSON = parseJsonFromText "Fork" +instance FromJSONKey Fork where + fromJSONKey = FromJSONKeyTextParser $ either fail return . eitherFromText + +newtype ChainwebVersionName = + ChainwebVersionName { getChainwebVersionName :: T.Text } + deriving stock (Generic, Eq, Ord) + deriving newtype (Show, ToJSON, FromJSON) + deriving anyclass (Hashable, NFData) --- | Generally, a chain is uniquely identified by it's genesis block. For efficiency --- and convenience we explicitely propagate 'ChainwebVersion' and the 'ChainId' --- to all blocks in the chain. At runtime the 'ChainId' is represented at --- the type level (but included as value in serialized representations). Thus, --- the ChainwebVersion identifies a chain at runtime at the value level. --- --- We assume that values that are identified through different Chainweb --- versions are not mixed at runtime. This is not enforced at the type level. --- -data ChainwebVersion - -------------------- - -- TESTING INSTANCES - -------------------- - = Test ChainGraph - -- ^ General-purpose test instance, where: - -- - -- * the underlying `ChainGraph` is configurable, - -- * the genesis block time is the Linux epoch, - -- * each `HashTarget` is maxBound, - -- * each mining `Nonce` is constant, - -- * the creationTime of `BlockHeader`s is the parent time plus one second, and - -- * POW is simulated by poison process thread delay. - -- - -- This is primarily used in unit tests. - -- - - | TimedConsensus ChainGraph ChainGraph - -- ^ Test instance for confirming the behaviour of our Consensus - -- mechanisms (Cut processing, Header validation, etc.), where: - -- - -- * the underlying `ChainGraph` is configurable, - -- * the genesis block time is the Linux epoch, - -- * each `HashTarget` is maxBound, - -- * each mining `Nonce` is constant, - -- * the creationTime of `BlockHeader`s is the actual time, - -- * POW is simulated by poison process thread delay, and - -- * there are /no/ Pact or mempool operations running. - -- - -- This is primarily used in our @slow-tests@ executable. - -- - - | PowConsensus ChainGraph - -- ^ Test instance for confirming the behaviour of the Proof-of-Work - -- mining algorithm and Difficulty Adjustment, where: - -- - -- * the underlying `ChainGraph` is configurable, - -- * the genesis block time the current time, - -- * the genesis `HashTarget` is 7 bits lower than maxBound, - -- * the `Nonce` changes with each mining attempt, - -- * creationTime of BlockHeaders is the actual time, and - -- * there are /no/ Pact or mempool operations running. - -- - -- This is primarily used in our @slow-tests@ executable. - -- - - | TimedCPM ChainGraph - -- ^ Test instance for confirming the combined behaviour of our Consensus - -- mechanisms, Pact code processing and validation, and Mempool, where: - -- - -- * the underlying `ChainGraph` is configurable, - -- * the genesis block time is the Linux epoch, - -- * each `HashTarget` is maxBound, - -- * each mining `Nonce` is constant, - -- * the creationTime of `BlockHeader`s is the actual time, - -- * POW is simulated by poison process thread delay, and - -- * the Pact Service and Mempool operations are running. - -- - -- This is primarily used in our @run-nodes@ executable. - -- - - | FastTimedCPM ChainGraph - -- ^ Test instance for confirming the combined behaviour of our Consensus - -- mechanisms, Pact code processing and validation, and Mempool, where: - -- - -- * the underlying `ChainGraph` is configurable, - -- * the genesis block time is the Linux epoch, - -- * each `HashTarget` is maxBound, - -- * each mining `Nonce` is constant, - -- * the creationTime of `BlockHeader`'s is the actual time, - -- * POW is not simulated by poison process thread delay, and - -- * the Pact Service and Mempool operations are running. - -- - -- This is primarily used in our @standalone@ executable. - -- - - ------------------------ - -- DEVELOPMENT INSTANCES - ------------------------ - | Development - -- ^ An instance which has no guarantees about the long-term stability - -- of its parameters. They are free to change as developers require. - - ----------------------- - -- PRODUCTION INSTANCES - ----------------------- - | Testnet04 - | Mainnet01 - deriving (Eq, Ord, Generic) +newtype ChainwebVersionCode = + ChainwebVersionCode { getChainwebVersionCode :: Word32 } + deriving stock (Generic, Eq, Ord) + deriving newtype (Show, ToJSON, FromJSON) deriving anyclass (Hashable, NFData) -instance Show ChainwebVersion where - show = T.unpack . toText - {-# INLINE show #-} +data Upgrade = Upgrade + { _upgradeTransactions :: [ChainwebTransaction] + -- edtodo document + , _legacyUpgradeIsPrecocious :: Bool + } + deriving stock (Generic) + deriving anyclass (NFData) --- | This function and its dual `fromChainwebVersionId` are used to efficiently --- serialize a `ChainwebVersion` and its associated internal `ChainGraph` value. --- __This function must be injective (one-to-one)!__ The scheme is as follows: --- --- * Production `ChainwebVersion`s start from @0x00000001@ and count upwards. --- Their value must be less than @0x8000000@, but this limit is unlikely to --- ever be reached. --- --- * `ChainwebVersion`s for testing begin at @0x80000000@, as can be seen in --- `toTestChainwebVersion`. This value is combined (via `.|.`) with the --- "code" of their associated `ChainGraph` (as seen in `graphToCode`). Such --- codes start at @0x00010000@ and count upwards. --- -chainwebVersionId :: ChainwebVersion -> Word32 -chainwebVersionId v@Test{} = toTestChainwebVersionId v -chainwebVersionId v@TimedConsensus{} = toTestChainwebVersionId v -chainwebVersionId v@PowConsensus{} = toTestChainwebVersionId v -chainwebVersionId v@TimedCPM{} = toTestChainwebVersionId v -chainwebVersionId v@FastTimedCPM{} = toTestChainwebVersionId v -chainwebVersionId Development = 0x00000001 -chainwebVersionId Testnet04 = 0x00000007 -chainwebVersionId Mainnet01 = 0x00000005 -{-# INLINABLE chainwebVersionId #-} - -fromChainwebVersionId :: HasCallStack => Word32 -> ChainwebVersion -fromChainwebVersionId 0x00000001 = Development -fromChainwebVersionId 0x00000007 = Testnet04 -fromChainwebVersionId 0x00000005 = Mainnet01 -fromChainwebVersionId i = fromTestChainwebVersionId i -{-# INLINABLE fromChainwebVersionId #-} - -encodeChainwebVersion :: ChainwebVersion -> Put -encodeChainwebVersion = putWord32le . chainwebVersionId -{-# INLINABLE encodeChainwebVersion #-} - -decodeChainwebVersion :: Get ChainwebVersion -decodeChainwebVersion = fromChainwebVersionId <$> getWord32le -{-# INLINABLE decodeChainwebVersion #-} - -instance ToJSON ChainwebVersion where - toJSON = toJSON . toText - toEncoding = toEncoding . toText - {-# INLINE toJSON #-} - {-# INLINE toEncoding #-} - -instance FromJSON ChainwebVersion where - parseJSON = parseJsonFromText "ChainwebVersion" - {-# INLINE parseJSON #-} - -instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVersion where - type Tag ChainwebVersion = 'ChainwebVersionTag - toMerkleNode = encodeMerkleInputNode encodeChainwebVersion - fromMerkleNode = decodeMerkleInputNode decodeChainwebVersion - {-# INLINE toMerkleNode #-} - {-# INLINE fromMerkleNode #-} - --- FIXME This doesn't warn of incomplete pattern matches upon the addition of a --- new `ChainwebVersion` value! -chainwebVersionToText :: HasCallStack => ChainwebVersion -> T.Text -chainwebVersionToText Development = "development" -chainwebVersionToText Testnet04 = "testnet04" -chainwebVersionToText Mainnet01 = "mainnet01" -chainwebVersionToText (Test g) = "test-" <> toText g -chainwebVersionToText (TimedConsensus g1 g2) = "timedConsensus-" <> toText g1 <> "-" <> toText g2 -chainwebVersionToText (PowConsensus g) = "powConsensus-" <> toText g -chainwebVersionToText (TimedCPM g) = "timedCPM-" <> toText g -chainwebVersionToText (FastTimedCPM g) = "fastTimedCPM-" <> toText g -{-# INLINABLE chainwebVersionToText #-} - --- | Read textual representation of a `ChainwebVersion`. --- --- NOTE: This doesn't warn of incomplete pattern matches upon the addition of a --- new `ChainwebVersion` value! --- -chainwebVersionFromText :: MonadThrow m => T.Text -> m ChainwebVersion -chainwebVersionFromText "development" = pure Development -chainwebVersionFromText "testnet04" = pure Testnet04 -chainwebVersionFromText "mainnet01" = pure Mainnet01 -chainwebVersionFromText t = case T.splitOn "-" t of - [ "test", g ] -> Test <$> fromText g - [ "timedConsensus", g1, g2 ] -> TimedConsensus <$> fromText g1 <*> fromText g2 - [ "powConsensus", g ] -> PowConsensus <$> fromText g - [ "timedCPM", g ] -> TimedCPM <$> fromText g - [ "fastTimedCPM", g ] -> FastTimedCPM <$> fromText g - _ -> throwM . TextFormatException $ "Unknown Chainweb version: " <> t - -instance HasTextRepresentation ChainwebVersion where - toText = chainwebVersionToText - {-# INLINE toText #-} - fromText = chainwebVersionFromText - {-# INLINE fromText #-} +upgrade :: [ChainwebTransaction] -> Upgrade +upgrade txs = Upgrade txs False --- -------------------------------------------------------------------------- -- --- Test instances --- --- The code in this section must not be called in production. --- +data ChainwebVersion + = ChainwebVersion + { _versionCode :: ChainwebVersionCode + , _versionName :: ChainwebVersionName + , _versionGraphs :: Rule BlockHeight ChainGraph + , _versionForks :: HashMap Fork (ChainMap BlockHeight) + , _versionUpgrades :: ChainMap (HashMap BlockHeight Upgrade) + , _versionBlockRate :: BlockRate + , _versionWindow :: Maybe WindowWidth + , _versionHeaderBaseSizeBytes :: Natural + -- ^ The size in bytes of the constant portion of the serialized header. This is + -- the header /without/ the adjacent hashes. + -- + -- NOTE: This is internal. For the actual size of the serialized header + -- use 'headerSizeBytes'. + , _versionMaxBlockGasLimit :: Rule BlockHeight (Maybe Natural) + , _versionFakeFirstEpochStart :: Bool + , _versionBootstraps :: [PeerInfo] + , _versionGenesis :: ChainwebGenesis + , _versionCheats :: Cheats + } + deriving stock (Generic) + deriving anyclass NFData -data GraphPos = P1 | P2 deriving (Bounded, Enum) +instance Show ChainwebVersion where + show = T.unpack . getChainwebVersionName . _versionName -graphToCodeN :: GraphPos -> KnownGraph -> Word32 -graphToCodeN p g = shiftL (graphToCode g) (4 * (4 + fromEnum p)) - where - graphToCode :: KnownGraph -> Word32 - graphToCode Singleton = 0x00000001 - graphToCode Pair = 0x00000002 - graphToCode Triangle = 0x00000003 - graphToCode Peterson = 0x00000004 - graphToCode Twenty = 0x00000005 - graphToCode HoffmanSingleton = 0x00000006 - -codeToGraphN :: HasCallStack => GraphPos -> Word32 -> KnownGraph -codeToGraphN p c = codeToGraph (shiftR c (4 * (4 + fromEnum p)) .&. 0x0000000f) - where - codeToGraph :: HasCallStack => Word32 -> KnownGraph - codeToGraph 0x00000001 = Singleton - codeToGraph 0x00000002 = Pair - codeToGraph 0x00000003 = Triangle - codeToGraph 0x00000004 = Peterson - codeToGraph 0x00000005 = Twenty - codeToGraph 0x00000006 = HoffmanSingleton - codeToGraph _ = error "Unknown Graph Code" - -toTestChainwebVersionId :: HasCallStack => ChainwebVersion -> Word32 -toTestChainwebVersionId (Test g) = 0x80000000 - .|. graphToCodeN P1 (view chainGraphKnown g) -toTestChainwebVersionId (TimedConsensus g1 g2) = 0x80000001 - .|. graphToCodeN P1 (view chainGraphKnown g1) - .|. graphToCodeN P2 (view chainGraphKnown g2) -toTestChainwebVersionId (PowConsensus g) = 0x80000002 - .|. graphToCodeN P1 (view chainGraphKnown g) -toTestChainwebVersionId (TimedCPM g) = 0x80000003 - .|. graphToCodeN P1 (view chainGraphKnown g) -toTestChainwebVersionId (FastTimedCPM g) = 0x80000004 - .|. graphToCodeN P1 (view chainGraphKnown g) -toTestChainwebVersionId Development = - error "Illegal ChainwebVersion passed to toTestChainwebVersion" -toTestChainwebVersionId Testnet04 = - error "Illegal ChainwebVersion passed to toTestChainwebVersion" -toTestChainwebVersionId Mainnet01 = - error "Illegal ChainwebVersion passed to toTestChainwebVersion" - -fromTestChainwebVersionId :: HasCallStack => Word32 -> ChainwebVersion -fromTestChainwebVersionId c = case v of - 0x80000000 -> Test (knownChainGraph $ codeToGraphN P1 g) - 0x80000001 -> TimedConsensus - (knownChainGraph $ codeToGraphN P1 g) - (knownChainGraph $ codeToGraphN P2 g) - 0x80000002 -> PowConsensus (knownChainGraph $ codeToGraphN P1 g) - 0x80000003 -> TimedCPM (knownChainGraph $ codeToGraphN P1 g) - 0x80000004 -> FastTimedCPM (knownChainGraph $ codeToGraphN P1 g) - _ -> error "Unknown ChainwebVersion Code" - where - (v, g) = (0xf000ffff .&. c, 0x0fff0000 .&. c) +window :: ChainwebVersion -> Maybe WindowWidth +window = _versionWindow --- -------------------------------------------------------------------------- -- --- Type level ChainwebVersion +blockRate :: ChainwebVersion -> BlockRate +blockRate = _versionBlockRate + +instance Ord ChainwebVersion where + v `compare` v' = fold + -- edtodo: doc + -- [ _versionCode v `compare` _versionCode v' + [ _versionName v `compare` _versionName v' + , _versionGraphs v `compare` _versionGraphs v' + , _versionForks v `compare` _versionForks v' + , _versionBlockRate v `compare` _versionBlockRate v' + , _versionWindow v `compare` _versionWindow v' + , _versionHeaderBaseSizeBytes v `compare` _versionHeaderBaseSizeBytes v' + , _versionMaxBlockGasLimit v `compare` _versionMaxBlockGasLimit v' + , _versionFakeFirstEpochStart v `compare` _versionFakeFirstEpochStart v' + , _versionBootstraps v `compare` _versionBootstraps v' + , _versionCheats v `compare` _versionCheats v' + ] + +instance Eq ChainwebVersion where + v == v' = compare v v' == EQ + +data Cheats = Cheats + { _disablePow :: Bool + , _disablePact :: Bool + , _disablePeerValidation :: Bool + , _disableMempool :: Bool + } + deriving stock (Generic, Eq, Ord, Show) + deriving anyclass (ToJSON, FromJSON, NFData) + +data ChainwebGenesis = ChainwebGenesis + { _genesisBlockTarget :: ChainMap HashTarget + , _genesisBlockPayload :: ChainMap PayloadWithOutputs + , _genesisTime :: ChainMap BlockCreationTime + } + deriving stock (Generic, Eq) + deriving anyclass NFData + +instance Show ChainwebGenesis where + show _ = "" + +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'ChainwebVersion +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'ChainwebGenesis +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'Cheats + +genesisBlockPayloadHash :: ChainwebVersion -> ChainId -> BlockPayloadHash +genesisBlockPayloadHash v cid = v ^?! versionGenesis . genesisBlockPayload . onChain cid . to _payloadWithOutputsPayloadHash + +-- | Empty payload marking no-op transaction payloads for deprecated +-- versions. +-- +emptyPayload :: PayloadWithOutputs +emptyPayload = PayloadWithOutputs mempty miner coinbase h i o + where + BlockPayload h i o = newBlockPayload miner coinbase mempty + miner = MinerData $ encodeToByteString noMiner + coinbase = noCoinbaseOutput + +-- -- | This function and its dual `fromChainwebVersionId` are used to efficiently +-- -- serialize a `ChainwebVersion` and its associated internal `ChainGraph` value. +-- -- __This function must be injective (one-to-one)!__ The scheme is as follows: +-- -- +-- -- * Production `ChainwebVersion`s start from @0x00000001@ and count upwards. +-- -- Their value must be less than @0x8000000@, but this limit is unlikely to +-- -- ever be reached. +-- -- +-- -- * `ChainwebVersion`s for testing begin at @0x80000000@, as can be seen in +-- -- `toTestChainwebVersion`. This value is combined (via `.|.`) with the +-- -- "code" of their associated `ChainGraph` (as seen in `graphToCode`). Such +-- -- codes start at @0x00010000@ and count upwards. +-- -- +-- -- chainwebVersionId :: ChainwebVersion dc -> Word32 +-- -- chainwebVersionId v@Test{} = toTestChainwebVersionId v +-- -- chainwebVersionId v@TimedConsensus{} = toTestChainwebVersionId v +-- -- chainwebVersionId v@PowConsensus{} = toTestChainwebVersionId v +-- -- chainwebVersionId v@TimedCPM{} = toTestChainwebVersionId v +-- -- chainwebVersionId v@FastTimedCPM{} = toTestChainwebVersionId v +-- -- chainwebVersionId Development{} = 0x00000001 +-- -- chainwebVersionId Testnet04 = 0x00000007 +-- -- chainwebVersionId Mainnet01 = 0x00000005 +-- -- {-# INLINABLE chainwebVersionId #-} + +encodeChainwebVersionCode :: ChainwebVersionCode -> Put +encodeChainwebVersionCode = putWord32le . getChainwebVersionCode + +decodeChainwebVersionCode :: Get ChainwebVersionCode +decodeChainwebVersionCode = ChainwebVersionCode <$> getWord32le + +instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVersionCode where + type Tag ChainwebVersionCode = 'ChainwebVersionTag + toMerkleNode = encodeMerkleInputNode encodeChainwebVersionCode + fromMerkleNode = decodeMerkleInputNode decodeChainwebVersionCode + +instance HasTextRepresentation ChainwebVersionName where + toText = getChainwebVersionName + fromText = pure . ChainwebVersionName + +-- -- -------------------------------------------------------------------------- -- +-- -- Test instances +-- -- +-- -- The code in this section must not be called in production. +-- -- + +-- data GraphPos = P1 | P2 deriving (Bounded, Enum) + +-- graphToCodeN :: GraphPos -> KnownGraph -> Word32 +-- graphToCodeN p g = shiftL (graphToCode g) (4 * (4 + fromEnum p)) +-- where +-- graphToCode :: KnownGraph -> Word32 +-- graphToCode Singleton = 0x00000001 +-- graphToCode Pair = 0x00000002 +-- graphToCode Triangle = 0x00000003 +-- graphToCode Peterson = 0x00000004 +-- graphToCode Twenty = 0x00000005 +-- graphToCode HoffmanSingleton = 0x00000006 + +-- codeToGraphN :: HasCallStack => GraphPos -> Word32 -> KnownGraph +-- codeToGraphN p c = codeToGraph (shiftR c (4 * (4 + fromEnum p)) .&. 0x0000000f) +-- where +-- codeToGraph :: HasCallStack => Word32 -> KnownGraph +-- codeToGraph 0x00000001 = Singleton +-- codeToGraph 0x00000002 = Pair +-- codeToGraph 0x00000003 = Triangle +-- codeToGraph 0x00000004 = Peterson +-- codeToGraph 0x00000005 = Twenty +-- codeToGraph 0x00000006 = HoffmanSingleton +-- codeToGraph _ = error "Unknown Graph Code" + +-- -- toTestChainwebVersionId :: HasCallStack => ChainwebVersion dc -> Word32 +-- -- toTestChainwebVersionId (Test g) = 0x80000000 +-- -- .|. graphToCodeN P1 (view chainGraphKnown g) +-- -- toTestChainwebVersionId (TimedConsensus g1 g2) = 0x80000001 +-- -- .|. graphToCodeN P1 (view chainGraphKnown g1) +-- -- .|. graphToCodeN P2 (view chainGraphKnown g2) +-- -- toTestChainwebVersionId (PowConsensus g) = 0x80000002 +-- -- .|. graphToCodeN P1 (view chainGraphKnown g) +-- -- toTestChainwebVersionId (TimedCPM g) = 0x80000003 +-- -- .|. graphToCodeN P1 (view chainGraphKnown g) +-- -- toTestChainwebVersionId (FastTimedCPM g) = 0x80000004 +-- -- .|. graphToCodeN P1 (view chainGraphKnown g) +-- -- toTestChainwebVersionId Development{} = +-- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" +-- -- toTestChainwebVersionId Testnet04 = +-- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" +-- -- toTestChainwebVersionId Mainnet01 = +-- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" + +-- -- fromTestChainwebVersionId :: HasCallStack => Word32 -> ChainwebVersionCode +-- -- fromTestChainwebVersionId c = case v of +-- -- 0x80000000 -> Test (knownChainGraph $ codeToGraphN P1 g) +-- -- 0x80000001 -> TimedConsensus +-- -- (knownChainGraph $ codeToGraphN P1 g) +-- -- (knownChainGraph $ codeToGraphN P2 g) +-- -- 0x80000002 -> PowConsensus (knownChainGraph $ codeToGraphN P1 g) +-- -- 0x80000003 -> TimedCPM (knownChainGraph $ codeToGraphN P1 g) +-- -- 0x80000004 -> FastTimedCPM (knownChainGraph $ codeToGraphN P1 g) +-- -- _ -> error "Unknown ChainwebVersion Code" +-- -- where +-- -- (v, g) = (0xf000ffff .&. c, 0x0fff0000 .&. c) + +-- -- -------------------------------------------------------------------------- -- +-- -- Type level ChainwebVersion newtype ChainwebVersionT = ChainwebVersionT Symbol @@ -442,11 +499,14 @@ instance (KnownSymbol n) => KnownChainwebVersionSymbol ('ChainwebVersionT n) whe chainwebVersionSymbolVal _ = T.pack $ symbolVal (Proxy @n) someChainwebVersionVal :: ChainwebVersion -> SomeChainwebVersionT -someChainwebVersionVal v = case someSymbolVal (sshow v) of +someChainwebVersionVal v = someChainwebVersionVal' (_versionName v) + +someChainwebVersionVal' :: ChainwebVersionName -> SomeChainwebVersionT +someChainwebVersionVal' v = case someSymbolVal (sshow v) of (SomeSymbol (Proxy :: Proxy v)) -> SomeChainwebVersionT (Proxy @('ChainwebVersionT v)) --- -------------------------------------------------------------------------- -- --- Singletons +-- -- -------------------------------------------------------------------------- -- +-- -- Singletons data instance Sing (v :: ChainwebVersionT) where SChainwebVersion :: KnownChainwebVersionSymbol v => Sing v @@ -457,103 +517,92 @@ instance KnownChainwebVersionSymbol v => SingI (v :: ChainwebVersionT) where sing = SChainwebVersion instance SingKind ChainwebVersionT where - type Demote ChainwebVersionT = ChainwebVersion + type Demote ChainwebVersionT = ChainwebVersionName fromSing (SChainwebVersion :: Sing v) = unsafeFromText . chainwebVersionSymbolVal $ Proxy @v - toSing n = case someChainwebVersionVal n of + toSing n = case someChainwebVersionVal' n of SomeChainwebVersionT p -> SomeSing (singByProxy p) - {-# INLINE fromSing #-} - {-# INLINE toSing #-} - pattern FromSingChainwebVersion :: Sing (n :: ChainwebVersionT) -> ChainwebVersion -pattern FromSingChainwebVersion sng <- ((\v -> withSomeSing v SomeSing) -> SomeSing sng) - where FromSingChainwebVersion sng = fromSing sng +pattern FromSingChainwebVersion sng <- ((\v -> withSomeSing (_versionName v) SomeSing) -> SomeSing sng) {-# COMPLETE FromSingChainwebVersion #-} --- -------------------------------------------------------------------------- -- --- HasChainwebVersion Class - +-- -- -------------------------------------------------------------------------- -- +-- -- HasChainwebVersion Class +-- class HasChainwebVersion a where _chainwebVersion :: a -> ChainwebVersion _chainwebVersion = view chainwebVersion - {-# INLINE _chainwebVersion #-} chainwebVersion :: Getter a ChainwebVersion chainwebVersion = to _chainwebVersion - {-# INLINE chainwebVersion #-} {-# MINIMAL _chainwebVersion | chainwebVersion #-} instance HasChainwebVersion ChainwebVersion where _chainwebVersion = id - {-# INLINE _chainwebVersion #-} -- | All known chainIds. This includes chains that are not yet "active". -- chainIds :: HasChainwebVersion v => v -> HS.HashSet ChainId -chainIds = graphChainIds . snd . NE.head . chainwebGraphs . _chainwebVersion -{-# INLINE chainIds #-} +chainIds = graphChainIds . snd . ruleHead . _versionGraphs . _chainwebVersion + +-- edtodo: doc +forkUpgrades + :: ChainwebVersion + -> [(Fork, ChainMap Upgrade)] + -> ChainMap (HashMap BlockHeight Upgrade) +forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) + -- upgrades must not conflict + -- upgrades must be ordered like the forks are + -- upgrades must not be empty + where + conflictError fork h = + error $ "conflicting upgrades at block height " <> show h <> " when adding upgrade for fork " <> show fork + emptyUpgradeError fork = + error $ "empty set of upgrade transactions for fork " <> show fork + go acc (fork, txsPerChain) = + HM.unionWith + (HM.unionWithKey (conflictError fork)) + acc newTxs + where + newTxs = HM.fromList $ + [ (cid, HM.singleton forkHeight upg) + | cid <- HM.keys acc + , Just upg <- [txsPerChain ^? onChain cid] + , not (null $ _upgradeTransactions upg) || emptyUpgradeError fork + , let forkHeight = v ^?! versionForks . at fork . _Just . onChain cid + , forkHeight /= maxBound + ] + +-- edtodo: document +latestBehaviorAt :: ChainwebVersion -> BlockHeight +latestBehaviorAt v = + foldlOf' changes maxBlockHeight 0 v + 1 + where + changes = fold + [ versionForks . folded . folded + , versionUpgrades . folded . ifolded . asIndex + , versionGraphs . to ruleHead . _1 . _Just + ] + maxBlockHeight x y + | x == maxBound = y + | y == maxBound = x + | otherwise = max x y mkChainId - :: MonadThrow m - => HasChainwebVersion v - => Integral i - => v - -> BlockHeight - -> i - -> m ChainId + :: (MonadThrow m, HasChainwebVersion v) + => v -> BlockHeight -> Word32 -> m ChainId mkChainId v h i = cid <$ checkWebChainId (chainGraphAt (_chainwebVersion v) h) cid where - cid = unsafeChainId (fromIntegral i) -{-# INLINE mkChainId #-} + cid = unsafeChainId i --- -------------------------------------------------------------------------- -- --- Properties of Chainweb Versions --- -------------------------------------------------------------------------- -- - --- -------------------------------------------------------------------------- -- --- Graph - --- | Graphs of chainweb version --- --- Invariants: --- --- * Entries are sorted by 'BlockHeight' in decreasing order. --- * The last entry is for 'BlockHeight' 0. --- * The graphs decrease in order. --- --- The functions provided in 'Chainweb.Version.Utils' are generally more --- convenient to use than this function. --- -chainwebGraphs :: ChainwebVersion -> NE.NonEmpty (BlockHeight, ChainGraph) -chainwebGraphs (Test g) = pure (0, g) -chainwebGraphs (TimedConsensus g1 g2) = (8, g2) NE.:| [ (0, g1) ] -chainwebGraphs (PowConsensus g) = pure (0, g) -chainwebGraphs (TimedCPM g) = pure (0, g) -chainwebGraphs (FastTimedCPM g) = pure (0, g) -chainwebGraphs Testnet04 = - ( to20ChainsTestnet, twentyChainGraph ) NE.:| - [ ( 0, petersonChainGraph ) ] -chainwebGraphs Mainnet01 = - ( to20ChainsMainnet, twentyChainGraph ) NE.:| - [ ( 0, petersonChainGraph ) ] -chainwebGraphs Development = - ( to20ChainsDevelopment, twentyChainGraph ) NE.:| - [ ( 0, petersonChainGraph ) ] -{-# INLINE chainwebGraphs #-} - -to20ChainsMainnet :: BlockHeight -to20ChainsMainnet = 852_054 -- 2020-08-20 16:00:00 - -to20ChainsTestnet :: BlockHeight -to20ChainsTestnet = 332_604 -- 2020-07-28 16:00:00 - -to20ChainsDevelopment :: BlockHeight -to20ChainsDevelopment = 60 +-- -- -------------------------------------------------------------------------- -- +-- -- Properties of Chainweb Versions +-- -- -------------------------------------------------------------------------- -- -- | Return the Graph History at a given block height in descending order. -- @@ -567,17 +616,14 @@ chainwebGraphsAt :: HasCallStack => ChainwebVersion -> BlockHeight - -> NE.NonEmpty (BlockHeight, ChainGraph) -chainwebGraphsAt v h = NE.fromList - $ NE.dropWhile ((> h) . fst) - $ chainwebGraphs v -{-# INLINE chainwebGraphsAt #-} + -> Rule BlockHeight ChainGraph +chainwebGraphsAt v h = + ruleDropWhile (> h) (_versionGraphs v) -- | The 'ChainGraph' for the given 'BlockHeight' -- chainGraphAt :: HasCallStack => ChainwebVersion -> BlockHeight -> ChainGraph -chainGraphAt v = snd . NE.head . chainwebGraphsAt v -{-# INLINE chainGraphAt #-} +chainGraphAt v = snd . ruleHead . chainwebGraphsAt v -- | The 'ChainGraph' for the given 'BlockHeight' -- @@ -588,514 +634,13 @@ chainGraphAt_ -> BlockHeight -> ChainGraph chainGraphAt_ = chainGraphAt . _chainwebVersion -{-# INLINE chainGraphAt_ #-} - --- | The genesis graph for a given Chain --- --- Invariant: --- --- * The given ChainId exists in the first graph of the graph history. --- (We generally assume that this invariant holds throughout the code base. --- It is enforced via the 'mkChainId' smart constructor for ChainId.) --- -genesisGraph - :: HasCallStack - => HasChainwebVersion v - => HasChainId c - => v - -> c - -> ChainGraph -genesisGraph v = chainGraphAt v_ . genesisHeight v_ . _chainId - where - v_ = _chainwebVersion v -{-# INLINE genesisGraph #-} instance HasChainGraph (ChainwebVersion, BlockHeight) where _chainGraph = uncurry chainGraphAt - {-# INLINE _chainGraph #-} - --- -------------------------------------------------------------------------- -- --- Genesis Height - --- | Returns the height of the genesis block for a chain. --- --- The implementation is somewhat expensive. With the current number of chains --- this isn't an issue. Otherwise the result should be hardcoded or memoized. --- --- TODO: memoize the genesis header for the production versions. Give this --- function a less attractive name and instead use this name to return the --- block height from the genesis header. --- --- Invariant: --- --- * The given ChainId exists in the first graph of the graph history. --- (We generally assume that this invariant holds throughout the code base. --- It is enforced via the 'mkChainId' smart constructor for ChainId.) --- -genesisHeight :: HasCallStack => ChainwebVersion -> ChainId -> BlockHeight -genesisHeight v c = fst - $ head - $ NE.dropWhile (not . flip isWebChain c . snd) - $ NE.reverse (chainwebGraphs v) - --- -------------------------------------------------------------------------- -- --- POW Parameters - --- | The gap in SECONDS that we desire between the Creation Time of subsequent --- blocks in some chain. --- -newtype BlockRate = BlockRate { _getBlockRate :: Seconds } - --- | The Proof-of-Work `BlockRate` for each `ChainwebVersion`. This is the --- number of seconds we expect to pass while a miner mines on various chains, --- eventually succeeding on one. --- -blockRate :: ChainwebVersion -> BlockRate -blockRate Test{} = BlockRate 0 -blockRate TimedConsensus{} = BlockRate 4 -blockRate PowConsensus{} = BlockRate 10 -blockRate TimedCPM{} = BlockRate 4 -blockRate FastTimedCPM{} = BlockRate 1 --- 120 blocks per hour, 2,880 per day, 20,160 per week, 1,048,320 per year. -blockRate Testnet04 = BlockRate 30 -blockRate Mainnet01 = BlockRate 30 -blockRate Development = BlockRate $ maybe 30 int customeDevnetRate - -customeDevnetRate :: Maybe Int -customeDevnetRate = - readMaybe =<< unsafeDupablePerformIO (lookupEnv "DEVELOPMENT_BLOCK_RATE") -{-# NOINLINE customeDevnetRate #-} - --- | The number of blocks to be mined after a difficulty adjustment, before --- considering a further adjustment. Critical for the "epoch-based" adjustment --- algorithm seen in `adjust`. --- -newtype WindowWidth = WindowWidth Natural - --- | The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. For chainwebs --- that do not expect to perform POW, this should be `Nothing`. --- -window :: ChainwebVersion -> Maybe WindowWidth -window Test{} = Nothing -window TimedConsensus{} = Nothing --- 5 blocks, should take 50 seconds. -window PowConsensus{} = Just $ WindowWidth 8 -window TimedCPM{} = Nothing -window FastTimedCPM{} = Nothing --- 120 blocks, should take 1 hour given a 30 second BlockRate. -window Development = Just $ WindowWidth 120 --- 120 blocks, should take 1 hour given a 30 second BlockRate. -window Testnet04 = Just $ WindowWidth 120 -window Mainnet01 = Just $ WindowWidth 120 - --- -------------------------------------------------------------------------- -- --- Header Serialization - --- | The size in bytes of the constant portion of the serialized header. This is --- the header /without/ the adjacent hashes. --- --- NOTE: This is an internal function. For the actual size of the serialized header --- use 'headerSizeBytes'. --- -headerBaseSizeBytes :: ChainwebVersion -> Natural -headerBaseSizeBytes _ = 318 - 110 - --- | This is an internal function. Use 'headerSizeBytes' instead. --- --- Postconditions: for all @v@ --- --- * @not . null $ headerSizes v@, and --- * @0 == (fst . last) (headerSizes v)@. --- --- Note that for all but genesis headers the number of adjacent hashes depends --- on the graph of the parent. --- -headerSizes :: ChainwebVersion -> NE.NonEmpty (BlockHeight, Natural) -headerSizes v = fmap (\g -> headerBaseSizeBytes v + 36 * degree g + 2) <$> chainwebGraphs v - --- | The size of the serialized block header. --- --- This function is safe because of the invariant of 'headerSize' that there --- exists and entry for block height 0. --- --- Note that for all but genesis headers the number of adjacent hashes depends --- on the graph of the parent. --- -headerSizeBytes - :: HasCallStack - => ChainwebVersion - -> ChainId - -> BlockHeight - -> Natural -headerSizeBytes v cid h = snd - $ head - $ NE.dropWhile ((> relevantHeight) . fst) - $ headerSizes v - where - relevantHeight - | genesisHeight v cid == h = h - | otherwise = h - 1 -{-# INLINE headerSizeBytes #-} --- | The size of the work bytes /without/ the preamble of the chain id and target --- --- The chain graph, and therefore also the header size, is constant for all --- blocks at the same height except for genesis blocks. Because genesis blocks --- are never mined, we can ignore this difference here and just return the --- result for chain 0. --- --- NOTE: For production versions we require that the value is constant for a --- given chainweb version. This would only ever change as part of the --- introduction of new block header format. --- -workSizeBytes - :: HasCallStack - => ChainwebVersion - -> BlockHeight - -> Natural -workSizeBytes v h = headerSizeBytes v (unsafeChainId 0) h - 32 -{-# INLINE workSizeBytes #-} - --- -------------------------------------------------------------------------- -- --- Pact Validation Parameters +maxBlockGasLimit :: ChainwebVersion -> BlockHeight -> Maybe Natural +maxBlockGasLimit v bh = case measureRule bh $ _versionMaxBlockGasLimit v of + Bottom limit -> limit + Top (_, limit) -> limit + Between (_, limit) _ -> limit --- | This the hard upper limit of the gas within a block. Blocks that use more --- gas are invalid and rejected. This limit is needed as a DOS protection. --- --- Smaller limits can be configured for creating new blocks. --- --- Before the chainweb-node 2.16 fork, there was no maximum block gas limit. --- -maxBlockGasLimit - :: ChainwebVersion - -> ChainId - -> BlockHeight - -> Maybe Natural -maxBlockGasLimit Mainnet01 _ bh = 180000 <$ guard (chainweb216Pact After Mainnet01 bh) -maxBlockGasLimit Testnet04 _ bh = 180000 <$ guard (chainweb216Pact After Testnet04 bh) -maxBlockGasLimit Development _ _ = Just 180000 -maxBlockGasLimit _ _ _ = Just 2_000000 - --- -------------------------------------------------------------------------- -- --- Pact Validation Guards - --- | Mainnet applied vlun797Fix at @[timeMicrosQQ| 2019-12-10T21:00:00.0 |]@. --- --- This function provides the block heights when the fix became effective on the --- respective chains. --- -vuln797Fix - :: ChainwebVersion - -> ChainId - -> BlockHeight - -> Bool -vuln797Fix Mainnet01 cid h - | cid == unsafeChainId 0 = h >= 121452 - | cid == unsafeChainId 1 = h >= 121452 - | cid == unsafeChainId 2 = h >= 121452 - | cid == unsafeChainId 3 = h >= 121451 - | cid == unsafeChainId 4 = h >= 121451 - | cid == unsafeChainId 5 = h >= 121452 - | cid == unsafeChainId 6 = h >= 121452 - | cid == unsafeChainId 7 = h >= 121451 - | cid == unsafeChainId 8 = h >= 121452 - | cid == unsafeChainId 9 = h >= 121451 -vuln797Fix _ _ _ = True -{-# INLINE vuln797Fix #-} - --- | Mainnet upgraded to coin v2 at time at @[timeMicrosQQ| 2019-12-17T15:00:00.0 |]@. --- --- This function provides the block heights when coin v2 became effective on the --- respective chains. --- -coinV2Upgrade - :: ChainwebVersion - -> ChainId - -> BlockHeight - -> Bool -coinV2Upgrade Mainnet01 cid h - | cid == unsafeChainId 0 = h == 140808 - | cid == unsafeChainId 1 = h == 140809 - | cid == unsafeChainId 2 = h == 140808 - | cid == unsafeChainId 3 = h == 140809 - | cid == unsafeChainId 4 = h == 140808 - | cid == unsafeChainId 5 = h == 140808 - | cid == unsafeChainId 6 = h == 140808 - | cid == unsafeChainId 7 = h == 140809 - | cid == unsafeChainId 8 = h == 140808 - | cid == unsafeChainId 9 = h == 140808 - -- new chains on mainnet start already with v2 deployed in the genesis block -coinV2Upgrade Testnet04 cid h - | chainIdInt @Int cid >= 10 && chainIdInt @Int cid < 20 = h == 337000 - | otherwise = h == 1 -coinV2Upgrade Development cid h - | cid == unsafeChainId 0 = h == 3 - | otherwise = h == 4 -coinV2Upgrade _ _ 1 = True -coinV2Upgrade _ _ _ = False - --- | 20-chain rebalance --- --- This function provides the block heights when remediations will be applied --- to correspond to genesis grants in the new chains. --- -to20ChainRebalance - :: ChainwebVersion - -> ChainId - -> BlockHeight - -> Bool -to20ChainRebalance Mainnet01 _ h = h == to20ChainsMainnet -to20ChainRebalance Testnet04 _ h = h == to20ChainsTestnet -to20ChainRebalance Development _ h = h == to20ChainsDevelopment -to20ChainRebalance _ _ 2 = True -to20ChainRebalance _ _ _ = False - --- | Preserve Pact bugs pre 1.6 chainweb version --- Mainnet 328000 ~ UTC Feb 20 15:36, EST Feb 20 10:56 --- -pactBackCompat_v16 :: ChainwebVersion -> BlockHeight -> Bool -pactBackCompat_v16 Mainnet01 h = h < 328000 -pactBackCompat_v16 _ _ = False - --- | Early versions of chainweb used the creation time of the current header --- for validation of pact tx creation time and TTL. Nowadays the times of --- the parent header a used. --- --- When this guard is enabled timing validation is skipped. --- -skipTxTimingValidation :: ChainwebVersion -> BlockHeight -> Bool -skipTxTimingValidation Mainnet01 h = h < 449940 -- ~ 2020-04-03T00:00:00Z -skipTxTimingValidation _ h = h <= 1 - -- For most chainweb versions there is a large gap between creation times of - -- the genesis blocks and the corresponding first blocks. - -- - -- Some tests fake block heights without updating pdData appropriately. This - -- causes tx validation at height 1, even though the block height is larger. - -- By using the current header time for the block of height <= 1 we relax - -- the tx timing checks a bit. - --- | Checks height after which module name fix in effect. --- -enableModuleNameFix :: ChainwebVersion -> BlockHeight -> Bool -enableModuleNameFix Mainnet01 bh = bh >= 448501 -- ~ 2020-04-02T12:00:00Z -enableModuleNameFix _ bh = bh >= 2 - --- | Related, later fix (Pact #801) --- -enableModuleNameFix2 :: ChainwebVersion -> BlockHeight -> Bool -enableModuleNameFix2 Mainnet01 bh = bh >= 752214 -- ~ 2020-07-17 0:00:00 UTC -enableModuleNameFix2 Testnet04 bh = bh >= 289966 -- ~ 2020-07-13 -enableModuleNameFix2 _ bh = bh >= 2 - --- | Turn on pact events in command output. -enablePactEvents :: ChainwebVersion -> BlockHeight -> Bool -enablePactEvents Mainnet01 bh = bh >= 1138000 -enablePactEvents Testnet04 bh = bh >= 660000 -enablePactEvents Development bh = bh >= 40 -enablePactEvents (FastTimedCPM g) bh - | g == singletonChainGraph || g == pairChainGraph = True - | g == petersonChainGraph = bh > 10 - | otherwise = False -enablePactEvents _ bh = bh >= 2 - --- | Bridge support: ETH and event SPV. -enableSPVBridge :: ChainwebVersion -> BlockHeight -> Bool -enableSPVBridge Mainnet01 = (>= 1_275_000) -- 2021-01-14T16:35:58 -enableSPVBridge Testnet04 = (>= 820_000) -- 2021-01-14T17:12:02 -enableSPVBridge Development = (>= 50) -enableSPVBridge (FastTimedCPM g) = const $ g == pairChainGraph || g == petersonChainGraph -enableSPVBridge _ = const True - -data AtOrAfter = At | After deriving (Eq,Show) - --- | Pact 4 / coin v3 fork -pact4coin3Upgrade :: AtOrAfter -> ChainwebVersion -> BlockHeight -> Bool -pact4coin3Upgrade aoa v h = case aoa of - At -> go (==) v h - After -> go (<) v h - where - go f Mainnet01 = f 1_722_500 -- 2021-06-19T03:34:05 - go f Testnet04 = f 1_261_000 -- 2021-06-17T15:54:14 - go f Development = f 80 - go f (FastTimedCPM g) | g == petersonChainGraph = f 20 - go f _ = f 4 - -- lowering this number causes some tests in Test.Pact.SPV to fail - -pact420Upgrade :: ChainwebVersion -> BlockHeight -> Bool -pact420Upgrade Mainnet01 = (>= 2_334_500) -- 2022-01-17T17:51:12 -pact420Upgrade Testnet04 = (>= 1_862_000) -- 2022-01-13T16:11:10 -pact420Upgrade Development = (>= 90) -pact420Upgrade (FastTimedCPM g) | g == petersonChainGraph = (>= 5) -pact420Upgrade _ = const True - -enforceKeysetFormats :: ChainwebVersion -> BlockHeight -> Bool -enforceKeysetFormats Mainnet01 = (>= 2_162_000) -- 2021-11-18T20:06:55 -enforceKeysetFormats Testnet04 = (>= 1_701_000) -- 2021-11-18T17:54:36 -enforceKeysetFormats Development = (>= 100) -enforceKeysetFormats (FastTimedCPM g) | g == petersonChainGraph = (>= 10) -enforceKeysetFormats _ = const True - -doCheckTxHash :: ChainwebVersion -> BlockHeight -> Bool -doCheckTxHash Mainnet01 = (>= 2_349_800) -- 2022-01-23T02:53:38 -doCheckTxHash Testnet04 = (>= 1_889_000) -- 2022-01-24T04:19:24 -doCheckTxHash Development = (>= 110) -doCheckTxHash (FastTimedCPM g) | g == petersonChainGraph = (>= 7) -doCheckTxHash _ = const True - --- | Pact changes for Chainweb 2.13 --- -chainweb213Pact :: ChainwebVersion -> BlockHeight -> Bool -chainweb213Pact Mainnet01 = (>= 2_447_315) -- 2022-02-26 00:00:00 -chainweb213Pact Testnet04 = (>= 1_974_556) -- 2022-02-25 00:00:00 -chainweb213Pact Development = (>= 95) -chainweb213Pact (FastTimedCPM g) | g == petersonChainGraph = (> 25) -chainweb213Pact _ = const True - --- | Fork for musl trans funs -pact44NewTrans :: ChainwebVersion -> BlockHeight -> Bool -pact44NewTrans Mainnet01 = (>= 2_965_885) -- Todo: add date -pact44NewTrans Testnet04 = (>= 2_500_369) -- Todo: add date -pact44NewTrans _ = const True - --- | Pact and coin contract changes for Chainweb 2.14 --- -chainweb214Pact - :: AtOrAfter - -> ChainwebVersion - -> BlockHeight - -> Bool -chainweb214Pact aoa v h = case aoa of - At -> go (==) v h - After -> go (<) v h - where - go f Mainnet01 = f 2605663 -- 2022-04-22T00:00:00Z - go f Testnet04 = f 2134331 -- 2022-04-21T12:00:00Z - go f Development = f 115 - go f (FastTimedCPM g) | g == petersonChainGraph = f 30 - go f _ = f 5 - --- | Pact and coin contract changes for Chainweb 2.15 --- -chainweb215Pact - :: AtOrAfter - -> ChainwebVersion - -> BlockHeight - -> Bool -chainweb215Pact aoa v h = case aoa of - At -> go (==) v h - After -> go (<) v h - where - go f Mainnet01 = f 2766630 -- 2022-06-17T00:00:00+00:00 - go f Testnet04 = f 2295437 -- 2022-06-16T12:00:00+00:00 - go f Development = f 165 - go f (FastTimedCPM g) | g == petersonChainGraph = f 35 - go f _ = f 10 - --- | Pact and coin contract changes for Chainweb 2.16 --- -chainweb216Pact - :: AtOrAfter - -> ChainwebVersion - -> BlockHeight - -> Bool -chainweb216Pact aoa v h = case aoa of - At -> go (==) v h - After -> go (<) v h - where - go f Mainnet01 = f 2988324 -- 2022-09-02 00:00:00+00:00 - go f Testnet04 = f 2516739 -- 2022-09-01 12:00:00+00:00 - go f Development = f 215 - go f (FastTimedCPM g) | g == petersonChainGraph = f 53 - go f _ = f 16 - -chainweb217Pact - :: AtOrAfter - -> ChainwebVersion - -> BlockHeight - -> Bool -chainweb217Pact aoa v h = case aoa of - At -> go (==) v h - After -> go (<) v h - where - go f Mainnet01 = f 3_250_348 -- 2022-12-02 00:00:00+00:00 - go f Testnet04 = f 2_777_367 -- 2022-12-01 12:00:00+00:00 - go f Development = f 470 - go f (FastTimedCPM g) | g == petersonChainGraph = f 55 - go f _ = f 20 - --- -------------------------------------------------------------------------- -- --- Header Validation Guards --- --- The guards in this section encode when changes to validation rules for data --- on the chain become effective. --- --- Only the following types are allowed as parameters for guards --- --- * BlockHeader, --- * ParentHeader, --- * BlockCreationTime, and --- * ParentCreationTime --- --- The result is a simple 'Bool'. --- --- Guards should have meaningful names and should be used in a way that all --- places in the code base that depend on the guard should reference the --- respective guard. That way all dependent code can be easily identified using --- ide tools, like for instance @grep@. --- --- Each guard should have a description that provides background for the change --- and provides all information needed for maintaining the code or code that --- depends on it. --- - --- | Turn off slow epochs (emergency DA) for blocks from 80,000 onwward. --- --- Emergency DA is considered a miss-feature. --- --- It's intended purpose is to prevent chain hopping attacks, where an attacker --- temporarily adds a large amount of hash power, thus increasing the --- difficulty. When the hash power is removed, the remaining hash power may not --- be enough to reach the next block in reasonable time. --- --- In practice, emergency DAs cause more problems than they solve. In --- particular, they increase the chance of deep forks. Also they make the --- behavior of the system unpredictable in states of emergency, when stability --- is usually more important than throughput. --- -slowEpochGuard - :: ChainwebVersion - -> BlockHeight - -- ^ BlockHeight of parent Header - -> Bool -slowEpochGuard Mainnet01 h = h < 80000 -slowEpochGuard _ _ = False -{-# INLINE slowEpochGuard #-} - --- | Use the current block time for computing epoch start date and --- target. --- --- When this guard is switched off, there will be a single epoch of just 119 --- blocks. The target computation won't compensate for that, since the effects --- are marginal. --- -oldTargetGuard :: ChainwebVersion -> BlockHeight -> Bool -oldTargetGuard Mainnet01 h = h < 452820 -- ~ 2020-04-04T00:00:00Z -oldTargetGuard _ _ = False -{-# INLINE oldTargetGuard #-} - --- | Skip validation of feature flags. --- --- Unused feature flag bits are supposed to be set to 0. As of Chainweb 1.7, the --- Feature Flag bytes and Nonce bytes have switched places in `BlockHeader`. For --- live chains, enforcing the following condition must be ignored for the --- historical blocks for which both the Nonce and Flags could be anything. --- -skipFeatureFlagValidationGuard :: ChainwebVersion -> BlockHeight -> Bool -skipFeatureFlagValidationGuard Mainnet01 h = h < 530500 -- ~ 2020-05-01T00:00:xxZ -skipFeatureFlagValidationGuard _ _ = False - -oldDaGuard :: ChainwebVersion -> BlockHeight -> Bool -oldDaGuard Mainnet01 h = h < 771_414 -- ~ 2020-07-23 16:00:00 -oldDaGuard Testnet04 h = h < 318_204 -- ~ 2020-07-23 16:00:00 -oldDaGuard Development h = h < 13 -- after DA at 10 -oldDaGuard _ _ = False diff --git a/src/Chainweb/Version/Codes.hs b/src/Chainweb/Version/Codes.hs new file mode 100644 index 0000000000..a4b6fefd24 --- /dev/null +++ b/src/Chainweb/Version/Codes.hs @@ -0,0 +1,3 @@ +module Chainweb.Version.Codes where + +mainnetCode = diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs new file mode 100644 index 0000000000..540a8299ae --- /dev/null +++ b/src/Chainweb/Version/Development.hs @@ -0,0 +1,100 @@ +{-# language NumericUnderscores #-} +{-# language PatternSynonyms #-} +{-# language OverloadedStrings #-} +{-# language ViewPatterns #-} +{-# language QuasiQuotes #-} + +module Chainweb.Version.Development(devnet, pattern Development) where + +import qualified Data.HashMap.Strict as HM + +import Chainweb.BlockCreationTime +import Chainweb.BlockHeight +import Chainweb.ChainId +import Chainweb.Difficulty +import Chainweb.Graph +import Chainweb.Time +import Chainweb.Utils.Rule +import Chainweb.Version + +import qualified Chainweb.BlockHeader.Genesis.Development0Payload as DN0 +import qualified Chainweb.BlockHeader.Genesis.DevelopmentNPayload as DNN +import qualified Chainweb.BlockHeader.Genesis.DevelopmentKADPayload as DNKAD +import qualified Chainweb.Pact.Transactions.DevelopmentTransactions as Devnet +import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 +import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 +import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 +import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD + +to20ChainsDevelopment :: BlockHeight +to20ChainsDevelopment = 12 + +pattern Development :: ChainwebVersion +pattern Development <- ((== devnet) -> True) where + Development = devnet + +devnet :: ChainwebVersion +devnet = ChainwebVersion + { _versionCode = ChainwebVersionCode 0x00000001 + , _versionName = ChainwebVersionName "development" + + , _versionForks = HM.unions + [ HM.fromList + [ (Chainweb217Pact, AllChains $ BlockHeight 20) + , (Chainweb216Pact, AllChains $ BlockHeight 19) + , (Chainweb215Pact, AllChains $ BlockHeight 18) + , (Chainweb214Pact, AllChains $ BlockHeight 17) + , (Chainweb213Pact, AllChains $ BlockHeight 16) + , (Pact420, AllChains $ BlockHeight 15) + , (Pact4Coin3, AllChains $ BlockHeight 14) + , (CoinV2, onChains $ concat + [ [(unsafeChainId 0, BlockHeight 3)] + , [(unsafeChainId i, BlockHeight 4) | i <- [1..19]] + ]) + ] + -- all unspecified forks start at block 1 + , HM.fromList [(fork, AllChains 1) | fork <- [minBound..maxBound]] + ] + + , _versionUpgrades = chainZip HM.union + (forkUpgrades devnet + [ (CoinV2, onChains $ concat + [ [(unsafeChainId 0, upgrade Devnet.transactions)] + , [(unsafeChainId i, upgrade Devnet.transactions) | i <- [1..9]] + ]) + , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) + , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) + , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) + ]) + (onChains [(unsafeChainId 0, HM.singleton to20ChainsDevelopment (upgrade MNKAD.transactions))]) + + , _versionGraphs = + (to20ChainsDevelopment, twentyChainGraph) `Above` + End petersonChainGraph + + , _versionBlockRate = BlockRate 30_000_000 + , _versionWindow = Just $ WindowWidth 120 + , _versionHeaderBaseSizeBytes = 318 - 110 + , _versionFakeFirstEpochStart = True + , _versionBootstraps = [] + , _versionGenesis = ChainwebGenesis + { _genesisBlockTarget = onChains $ concat + [ [(unsafeChainId i, HashTarget $ maxBound `div` 100_000) | i <- [0..9]] + , [(unsafeChainId i, HashTarget 0x0000088f99632cadf39b0db7655be62cb7dbc84ebbd9a90e5b5756d3e7d9196c) | i <- [10..19]] + ] + , _genesisTime = AllChains $ BlockCreationTime [timeMicrosQQ| 2019-07-17T18:28:37.613832 |] + , _genesisBlockPayload = onChains $ concat + [ [(unsafeChainId 0, DN0.payloadBlock)] + , [(unsafeChainId i, DNN.payloadBlock) | i <- [1..9]] + , [(unsafeChainId i, DNKAD.payloadBlock) | i <- [10..19]] + ] + } + + , _versionMaxBlockGasLimit = End (Just 180_000) + , _versionCheats = Cheats + { _disablePeerValidation = True + , _disablePow = False + , _disablePact = False + , _disableMempool = False + } + } diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs new file mode 100644 index 0000000000..00028e1676 --- /dev/null +++ b/src/Chainweb/Version/Guards.hs @@ -0,0 +1,119 @@ +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +-- | +-- Module: Chainweb.Version.Guards +-- Copyright: Copyright © 2023 Kadena LLC. +-- License: MIT +-- Maintainer: Edmund Noble +-- Stability: experimental +-- +-- Functions which dictate changes in block validation at different BlockHeights, based on +-- chainweb versions. +-- + +module Chainweb.Version.Guards + -- ** Payload Validation Guards + ( vuln797Fix + -- , coinV2Upgrade + -- , to20ChainRebalance + , pactBackCompat_v16 + , skipTxTimingValidation + , enableModuleNameFix + , enableModuleNameFix2 + , enablePactEvents + , enableSPVBridge + , pact4Coin3 + , pact420 + , enforceKeysetFormats + , doCheckTxHash + , chainweb213Pact + , chainweb214Pact + , chainweb215Pact + , chainweb216Pact + , chainweb217Pact + , cleanModuleCache + , pact44NewTrans + , pactParserVersion + + -- ** BlockHeader Validation Guards + , slowEpochGuard + , oldTargetGuard + , skipFeatureFlagValidationGuard + , oldDaGuard + ) where + +import Control.Lens + +import Chainweb.BlockHeight +import Chainweb.ChainId +import Chainweb.Transaction +import Chainweb.Version + +getForkHeight :: Fork -> ChainwebVersion -> ChainId -> BlockHeight +getForkHeight fork v cid = v ^?! versionForks . at fork . _Just . onChain cid + +checkFork + :: (BlockHeight -> BlockHeight -> Bool) + -> Fork -> ChainwebVersion -> ChainId -> BlockHeight -> Bool +checkFork p f v cid h = p h (getForkHeight f v cid) + +vuln797Fix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +vuln797Fix = checkFork (>=) Vuln797Fix +pactBackCompat_v16 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pactBackCompat_v16 = checkFork (<) PactBackCompat_v16 +skipTxTimingValidation :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +skipTxTimingValidation = checkFork (<) SkipTxTimingValidation +enableModuleNameFix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +enableModuleNameFix = checkFork (>=) ModuleNameFix +enableModuleNameFix2 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +enableModuleNameFix2 = checkFork (>=) ModuleNameFix2 +enablePactEvents :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +enablePactEvents = checkFork (>=) PactEvents +enableSPVBridge :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +enableSPVBridge = checkFork (>=) SPVBridge +enforceKeysetFormats :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +enforceKeysetFormats = checkFork (>=) EnforceKeysetFormats +doCheckTxHash :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +doCheckTxHash = checkFork (>=) CheckTxHash +pact44NewTrans :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pact44NewTrans = checkFork (>=) Pact44NewTrans + +slowEpochGuard + :: ChainwebVersion + -> ChainId + -> BlockHeight + -- ^ BlockHeight of parent Header + -> Bool +slowEpochGuard = checkFork (<) SlowEpoch + +oldTargetGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +oldTargetGuard = checkFork (<) OldTargetGuard +skipFeatureFlagValidationGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +skipFeatureFlagValidationGuard = checkFork (<) SkipFeatureFlagValidation +oldDaGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +oldDaGuard = checkFork (<) OldDAGuard +pact4Coin3 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pact4Coin3 = checkFork (>) Pact4Coin3 +pact420 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pact420 = checkFork (>=) Pact420 +chainweb213Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb213Pact = checkFork (>=) Chainweb213Pact +chainweb214Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb214Pact = checkFork (>) Chainweb214Pact +chainweb215Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb215Pact = checkFork (>) Chainweb215Pact +chainweb216Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb216Pact = checkFork (>) Chainweb216Pact +chainweb217Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb217Pact = checkFork (>) Chainweb217Pact +cleanModuleCache :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +cleanModuleCache = checkFork (==) Chainweb217Pact + +pactParserVersion :: ChainwebVersion -> ChainId -> BlockHeight -> PactParserVersion +pactParserVersion v cid bh + | chainweb213Pact v cid bh = PactParserChainweb213 + | otherwise = PactParserGenesis diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs new file mode 100644 index 0000000000..057ee0f670 --- /dev/null +++ b/src/Chainweb/Version/Mainnet.hs @@ -0,0 +1,196 @@ +{-# language NumericUnderscores #-} +{-# language PatternSynonyms #-} +{-# language OverloadedStrings #-} +{-# language ViewPatterns #-} +{-# language QuasiQuotes #-} + +module Chainweb.Version.Mainnet(mainnet, pattern Mainnet01) where + +import Control.Lens +import qualified Data.HashMap.Strict as HM + +import Chainweb.BlockCreationTime +import Chainweb.BlockHeight +import Chainweb.ChainId +import Chainweb.Difficulty +import Chainweb.Graph +import Chainweb.Time +import Chainweb.Utils.Rule +import Chainweb.Version +import P2P.BootstrapNodes + +import qualified Chainweb.BlockHeader.Genesis.Mainnet0Payload as MN0 +import qualified Chainweb.BlockHeader.Genesis.Mainnet1Payload as MN1 +import qualified Chainweb.BlockHeader.Genesis.Mainnet2Payload as MN2 +import qualified Chainweb.BlockHeader.Genesis.Mainnet3Payload as MN3 +import qualified Chainweb.BlockHeader.Genesis.Mainnet4Payload as MN4 +import qualified Chainweb.BlockHeader.Genesis.Mainnet5Payload as MN5 +import qualified Chainweb.BlockHeader.Genesis.Mainnet6Payload as MN6 +import qualified Chainweb.BlockHeader.Genesis.Mainnet7Payload as MN7 +import qualified Chainweb.BlockHeader.Genesis.Mainnet8Payload as MN8 +import qualified Chainweb.BlockHeader.Genesis.Mainnet9Payload as MN9 +import qualified Chainweb.BlockHeader.Genesis.MainnetKADPayload as MNKAD +import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 +import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 +import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 +import qualified Chainweb.Pact.Transactions.Mainnet0Transactions as MN0 +import qualified Chainweb.Pact.Transactions.Mainnet1Transactions as MN1 +import qualified Chainweb.Pact.Transactions.Mainnet2Transactions as MN2 +import qualified Chainweb.Pact.Transactions.Mainnet3Transactions as MN3 +import qualified Chainweb.Pact.Transactions.Mainnet4Transactions as MN4 +import qualified Chainweb.Pact.Transactions.Mainnet5Transactions as MN5 +import qualified Chainweb.Pact.Transactions.Mainnet6Transactions as MN6 +import qualified Chainweb.Pact.Transactions.Mainnet7Transactions as MN7 +import qualified Chainweb.Pact.Transactions.Mainnet8Transactions as MN8 +import qualified Chainweb.Pact.Transactions.Mainnet9Transactions as MN9 +import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD + +-- | Initial hash target for mainnet 20-chain transition. Difficulty on the new +-- chains is 1/4 of the current difficulty. It is based on the following header +-- from 2020-07-09. This value should be double checked after the testnet +-- transition and before the release of chainweb node version 2.1. +-- +-- @ +-- { +-- "creationTime": 1594319266887602, +-- "parent": "aSIkDjuJQGGOwJW-60T_1WRK9KPJm1rz63a4SW8WtSc", +-- "height": 731382, +-- "hash": "Ua_pSMMo-szlMpXMuSYWTcVlaSIf01TxJvBCmFkmhBM", +-- "chainId": 0, +-- "weight": "xo3dabqEYpUPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", +-- "featureFlags": 0, +-- "epochStart": 1594316109999615, +-- "adjacents": { +-- "2": "KuuujcD6yeZ9jRXwlRE0ed5dHc3x_akIz1REmKXuDtk", +-- "5": "qFU32Qmlj-syzuZ2awCvyoW6Jex3TQqGTzd-Dchn1gc", +-- "3": "Lgu1FgiCw4qPpptoVRmijn8WKG2OcAUAp1Ha7KFbrWg" +-- }, +-- "payloadHash": "MV079yClHYSYBW74WySK-15AUVQg8QMKHJZbtzTCbgA", +-- "chainwebVersion": "mainnet01", +-- "target": "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA", +-- "nonce": "149742924667593745" +-- } +-- @ +-- +-- It holds that: +-- +-- prop> Just mainnet20InitialHashTarget == HashTarget . (4 *) <$> (runGet decodePowHashNat =<< decodeB64UrlNoPaddingText "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") +-- +mainnet20InitialHashTarget :: HashTarget +mainnet20InitialHashTarget = HashTarget 0x000000000000cb73de4be95ba21db5b9178dd85974c194e3ee05717dd8afa830 + +to20ChainsMainnet :: BlockHeight +to20ChainsMainnet = 852_054 -- 2020-08-20 16:00:00 + +pattern Mainnet01 :: ChainwebVersion +pattern Mainnet01 <- ((== mainnet) -> True) where + Mainnet01 = mainnet + +mainnet :: ChainwebVersion +mainnet = ChainwebVersion + { _versionCode = ChainwebVersionCode 0x00000005 + , _versionName = ChainwebVersionName "mainnet01" + , _versionForks = HM.fromList + [ (Chainweb217Pact, AllChains (BlockHeight 3_250_348)) -- 2022-12-02T00:00:00+00:00 + , (Chainweb216Pact, AllChains (BlockHeight 2_988_324)) -- 2022-09-02T00:00:00+00:00 + , (Chainweb215Pact, AllChains (BlockHeight 2_766_630)) -- 2022-06-17T00:00:00+00:00 + , (Chainweb214Pact, AllChains (BlockHeight 2_605_663)) -- 2022-04-22T00:00:00+00:00 + , (Chainweb213Pact, AllChains (BlockHeight 2_447_315)) -- 2022-02-26T00:00:00+00:00 + , (Pact420, AllChains (BlockHeight 2_334_500)) -- 2022-01-17T17:51:12+00:00 + , (Pact4Coin3, AllChains (BlockHeight 1_722_500)) -- 2021-06-19T03:34:05+00:00 + , (CoinV2, onChains $ + [ (unsafeChainId 0, BlockHeight 140_808) + , (unsafeChainId 1, BlockHeight 140_809) + , (unsafeChainId 2, BlockHeight 140_808) + , (unsafeChainId 3, BlockHeight 140_809) + , (unsafeChainId 4, BlockHeight 140_808) + , (unsafeChainId 5, BlockHeight 140_808) + , (unsafeChainId 6, BlockHeight 140_808) + , (unsafeChainId 7, BlockHeight 140_809) + , (unsafeChainId 8, BlockHeight 140_808) + , (unsafeChainId 9, BlockHeight 140_808) + ]) + , (SlowEpoch, AllChains 80_000) + , (OldTargetGuard, AllChains 452_820) -- ~ 2020-04-04T00:00:00Z + , (SkipFeatureFlagValidation, AllChains 530_500) -- ~ 2020-05-01T00:00:xxZ + , (OldDAGuard, AllChains 771_414) -- ~ 2020-07-23 16:00:00 + , (Vuln797Fix, onChains $ + [ (unsafeChainId 0, BlockHeight 121_452) -- 2019-12-10T21:00:00.0 + , (unsafeChainId 1, BlockHeight 121_452) + , (unsafeChainId 2, BlockHeight 121_452) + , (unsafeChainId 3, BlockHeight 121_451) + , (unsafeChainId 4, BlockHeight 121_451) + , (unsafeChainId 5, BlockHeight 121_452) + , (unsafeChainId 6, BlockHeight 121_452) + , (unsafeChainId 7, BlockHeight 121_451) + , (unsafeChainId 8, BlockHeight 121_452) + , (unsafeChainId 9, BlockHeight 121_451) + ] <> [(unsafeChainId i, BlockHeight 0) | i <- [10..19]]) + , (PactBackCompat_v16, AllChains 328_000) + , (SkipTxTimingValidation, AllChains 449_940) + , (ModuleNameFix, AllChains 448_501) + , (ModuleNameFix2, AllChains 752_214) + , (PactEvents, AllChains 1_138_000) + , (SPVBridge, AllChains 1_275_000) + , (EnforceKeysetFormats, AllChains 2_162_000) -- 2022-01-17T17:51:12 + , (CheckTxHash, AllChains 2_349_800) -- 2022-01-23T02:53:38 + , (Pact44NewTrans, AllChains 2_965_885) -- Todo: add date + ] + , _versionGraphs = + (to20ChainsMainnet, twentyChainGraph) `Above` + End petersonChainGraph + , _versionBlockRate = BlockRate 30_000_000 + , _versionWindow = Just $ WindowWidth 120 + , _versionHeaderBaseSizeBytes = 318 - 110 + , _versionMaxBlockGasLimit = + (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` + End Nothing + , _versionFakeFirstEpochStart = False + , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts + , _versionGenesis = ChainwebGenesis + { _genesisBlockTarget = OnChains $ HM.fromList $ concat + [ [(unsafeChainId i, maxTarget) | i <- [0..9]] + , [(unsafeChainId i, mainnet20InitialHashTarget) | i <- [10..19]] + ] + , _genesisTime = AllChains $ BlockCreationTime [timeMicrosQQ| 2019-10-30T00:01:00.0 |] + , _genesisBlockPayload = OnChains $ HM.fromList $ concat + [ [ (unsafeChainId 0, MN0.payloadBlock) + , (unsafeChainId 1, MN1.payloadBlock) + , (unsafeChainId 2, MN2.payloadBlock) + , (unsafeChainId 3, MN3.payloadBlock) + , (unsafeChainId 4, MN4.payloadBlock) + , (unsafeChainId 5, MN5.payloadBlock) + , (unsafeChainId 6, MN6.payloadBlock) + , (unsafeChainId 7, MN7.payloadBlock) + , (unsafeChainId 8, MN8.payloadBlock) + , (unsafeChainId 9, MN9.payloadBlock) + ] + , [(unsafeChainId i, MNKAD.payloadBlock) | i <- [10..19]] + ] + } + , _versionUpgrades = chainZip HM.union + (forkUpgrades mainnet + [ (CoinV2, onChains + [ (unsafeChainId 0, upgrade MN0.transactions) + , (unsafeChainId 1, upgrade MN1.transactions) + , (unsafeChainId 2, upgrade MN2.transactions) + , (unsafeChainId 3, upgrade MN3.transactions) + , (unsafeChainId 4, upgrade MN4.transactions) + , (unsafeChainId 5, upgrade MN5.transactions) + , (unsafeChainId 6, upgrade MN6.transactions) + , (unsafeChainId 7, upgrade MN7.transactions) + , (unsafeChainId 8, upgrade MN8.transactions) + , (unsafeChainId 9, upgrade MN9.transactions) + ]) + , (Pact4Coin3, AllChains $ Upgrade CoinV3.transactions True) + , (Chainweb214Pact, AllChains $ Upgrade CoinV4.transactions True) + , (Chainweb215Pact, AllChains $ Upgrade CoinV5.transactions True) + ]) + (onChains [(unsafeChainId 0, HM.singleton to20ChainsMainnet (upgrade MNKAD.transactions))]) + , _versionCheats = Cheats + { _disablePeerValidation = False + , _disablePow = False + , _disableMempool = False + , _disablePact = False + } + } diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs new file mode 100644 index 0000000000..0a019d7bcf --- /dev/null +++ b/src/Chainweb/Version/Registry.hs @@ -0,0 +1,69 @@ +{-# language RecordWildCards #-} + +module Chainweb.Version.Registry + ( registerVersion + , validateVersion + , lookupVersionByCode + , knownVersions + , findKnownVersion + , versionMap + ) where + +import Control.DeepSeq +import Control.Exception +import Data.Foldable +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HM +import Data.IORef +import Data.Maybe +import qualified Data.Text as T +import System.IO.Unsafe + +import GHC.Stack + +import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet + +{-# NOINLINE versionMap #-} +versionMap :: IORef (HashMap ChainwebVersionCode ChainwebVersion) +versionMap = unsafePerformIO $ do + traverse_ validateVersion knownVersions + newIORef HM.empty + +-- disallow duplicates +registerVersion :: ChainwebVersion -> IO () +registerVersion v = do + validateVersion v + atomicModifyIORef' versionMap $ \m -> + case HM.lookup (_versionCode v) m of + Just v' + | v /= v' -> error "registerVersion: conflicting version registered already" + | otherwise -> (m, ()) + Nothing -> + (HM.insert (_versionCode v) v m, ()) + +validateVersion :: ChainwebVersion -> IO () +validateVersion v = do -- edtodo + evaluate (rnf v) + +-- edtodo: doc +lookupVersionByCode :: HasCallStack => ChainwebVersionCode -> ChainwebVersion +lookupVersionByCode code + | Just v <- find (\v -> _versionCode v == code) knownVersions = v + | otherwise = lazify (unsafeDupablePerformIO $ do + m <- readIORef versionMap + return $ fromMaybe (error $ "version not registered with code " <> show code) $ HM.lookup code m + ) { _versionCode = code } + where + lazify ~ChainwebVersion{..} = ChainwebVersion{..} + +knownVersions :: [ChainwebVersion] +knownVersions = [mainnet, testnet, devnet] + +findKnownVersion :: MonadFail m => ChainwebVersionName -> m ChainwebVersion +findKnownVersion vn = + case find (\v -> _versionName v == vn) knownVersions of + Nothing -> fail $ T.unpack (getChainwebVersionName vn) <> " is not a known version: try development, mainnet01 or testnet04" + Just v -> return v diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs new file mode 100644 index 0000000000..ff64106ef3 --- /dev/null +++ b/src/Chainweb/Version/Testnet.hs @@ -0,0 +1,166 @@ +{-# language NumericUnderscores #-} +{-# language PatternSynonyms #-} +{-# language OverloadedStrings #-} +{-# language ViewPatterns #-} +{-# language QuasiQuotes #-} + +module Chainweb.Version.Testnet(testnet, pattern Testnet04) where + +import Control.Lens +import qualified Data.HashMap.Strict as HM + +import Chainweb.BlockCreationTime +import Chainweb.BlockHeight +import Chainweb.ChainId +import Chainweb.Difficulty +import Chainweb.Graph +import Chainweb.Time +import Chainweb.Utils.Rule +import Chainweb.Version +import P2P.BootstrapNodes + +import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 +import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 +import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 +import qualified Chainweb.Pact.Transactions.Mainnet0Transactions as MN0 +import qualified Chainweb.Pact.Transactions.Mainnet1Transactions as MN1 +import qualified Chainweb.Pact.Transactions.Mainnet2Transactions as MN2 +import qualified Chainweb.Pact.Transactions.Mainnet3Transactions as MN3 +import qualified Chainweb.Pact.Transactions.Mainnet4Transactions as MN4 +import qualified Chainweb.Pact.Transactions.Mainnet5Transactions as MN5 +import qualified Chainweb.Pact.Transactions.Mainnet6Transactions as MN6 +import qualified Chainweb.Pact.Transactions.Mainnet7Transactions as MN7 +import qualified Chainweb.Pact.Transactions.Mainnet8Transactions as MN8 +import qualified Chainweb.Pact.Transactions.Mainnet9Transactions as MN9 +import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD +import qualified Chainweb.BlockHeader.Genesis.Testnet0Payload as PN0 +import qualified Chainweb.BlockHeader.Genesis.TestnetNPayload as PNN + +-- | Initial hash target for testnet 20-chain transition. Based on the following +-- header from devnet running with 5 GPUs hash power. Using this target unchanged +-- means, that we should do to the transition with the hash power of about +-- 5 - 50 GPUs in the system for a smooth transition. +-- +-- The value for the initial target is 38 times smaller larger than value of an +-- successful test run on devnet with 5 GPUs. During that test the initial +-- target was about 32 times larger than the actual target at the time of the +-- transition. +-- +-- @ +-- { +-- "creationTime": 1594433454304125, +-- "parent": "DHSarVwhj6Xvu0KewCI1nRdGcNSWKFoOUy7us27mDac", +-- "height": 200, +-- "hash": "DC8HV9W0JM5gzliwDupjG10Lnwav09xWtxy01kGPTLM", +-- "chainId": 0, +-- "weight": "ReZ2aCAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", +-- "featureFlags": 0, +-- "epochStart": 1594430808323849, +-- "adjacents": { +-- "2": "JPbz_YjWIvDgdGxdemkU6vVRimZZawxY_j0Hwo0pzb0", +-- "5": "wMFfoFrQ1GWOFj6jCNGRa3SiuFRGOCmS06F7HfmLnNw", +-- "3": "9WIBnxDGGZsy9FCCorvAUa4SlE5Rqs-cTLEsWCPOVbQ" +-- }, +-- "payloadHash": "AOYQdE5xl_YueZSppW4MoadasjF149K28CON2GuH9Mc", +-- "chainwebVersion": "development", +-- "target": "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA", +-- "nonce": "5805155470630695" +-- } +-- @ +-- +-- It holds that: +-- +-- prop> Just testnet20InitialHashTarget == HashTarget <$> (runGet decodePowHashNat =<< decodeB64UrlNoPaddingText "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") +-- prop> _hashTarget testnet20InitialHashTarget `div` _hashTarget mainnet20InitialHashTarget == PowHashNat 8893 +-- prop> _hashTarget (genesisBlockTarget Development (unsafeChainId 10)) `div` _hashTarget testnet20InitialHashTarget == PowHashNat 38 +-- +testnet20InitialHashTarget :: HashTarget +testnet20InitialHashTarget = HashTarget 0x000000001b9bf15be43824bae4c4f17722572883f7b53ed2e8c6ba9596249235 + +to20ChainsTestnet :: BlockHeight +to20ChainsTestnet = 332_604 -- 2020-07-28 16:00:00 + +pattern Testnet04 :: ChainwebVersion +pattern Testnet04 <- ((== testnet) -> True) where + Testnet04 = testnet + +testnet :: ChainwebVersion +testnet = ChainwebVersion + { _versionCode = ChainwebVersionCode 0x00000007 + , _versionName = ChainwebVersionName "testnet04" + , _versionForks = HM.fromList + [ (Chainweb217Pact, AllChains 2_777_367) -- 2022-12-01 12:00:00+00:00 + , (Chainweb216Pact, AllChains 2_516_739) -- 2022-09-01 12:00:00+00:00 + , (Chainweb215Pact, AllChains 2_295_437) -- 2022-06-16T12:00:00+00:00 + , (Chainweb214Pact, AllChains 2_134_331) -- 2022-04-21T12:00:00Z + , (Chainweb213Pact, AllChains 1_974_556) -- 2022-02-25 00:00:00 + , (Pact420, AllChains 1_862_000) -- 2021-06-19T03:34:05 + , (Pact4Coin3, AllChains 1_261_000) -- 2021-06-17T15:54:14 + , (CoinV2, onChains $ concat + [ [(unsafeChainId i, BlockHeight 1) | i <- [0..9]] + , [(unsafeChainId i, BlockHeight 337_000) | i <- [10..19]] + ]) + , (SlowEpoch, AllChains 0) + , (OldTargetGuard, AllChains 0) + , (SkipFeatureFlagValidation, AllChains 0) + , (OldDAGuard, AllChains 318_204) -- ~ 2020-07-23 16:00:00 + , (Vuln797Fix, AllChains 0) + , (PactBackCompat_v16, AllChains 0) + , (SkipTxTimingValidation, AllChains 1) + , (ModuleNameFix, AllChains 2) + , (ModuleNameFix2, AllChains 289_966) -- ~ 2020-07-13 + , (PactEvents, AllChains 660_000) + , (SPVBridge, AllChains 820_000) -- 2021-01-14T17:12:02 + , (EnforceKeysetFormats, AllChains 1_701_000) -- 2021-11-18T17:54:36 + , (CheckTxHash, AllChains 1_889_000) -- 2022-01-24T04:19:24 + , (Pact44NewTrans, AllChains 2_500_369) -- Todo: add date + ] + , _versionGraphs = + (to20ChainsTestnet, twentyChainGraph) `Above` + End petersonChainGraph + , _versionBlockRate = BlockRate 30_000_000 + , _versionWindow = Just $ WindowWidth 120 + , _versionHeaderBaseSizeBytes = 318 - 110 + , _versionMaxBlockGasLimit = + (succ $ testnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` + End Nothing + , _versionFakeFirstEpochStart = False + , _versionBootstraps = domainAddr2PeerInfo testnetBootstrapHosts + , _versionGenesis = ChainwebGenesis + { _genesisBlockTarget = OnChains $ HM.fromList $ concat + [ [(unsafeChainId i, maxTarget) | i <- [0..9]] + , [(unsafeChainId i, testnet20InitialHashTarget) | i <- [10..19]] + ] + , _genesisTime = AllChains $ BlockCreationTime [timeMicrosQQ| 2019-07-17T18:28:37.613832 |] + , _genesisBlockPayload = OnChains $ HM.fromList $ concat + [ [ (unsafeChainId 0, PN0.payloadBlock) + ] + , [(unsafeChainId i, PNN.payloadBlock) | i <- [1..19]] + ] + } + , _versionUpgrades = chainZip HM.union + (forkUpgrades testnet + [ (CoinV2, onChains $ + [ (unsafeChainId 0, upgrade MN0.transactions) + , (unsafeChainId 1, upgrade MN1.transactions) + , (unsafeChainId 2, upgrade MN2.transactions) + , (unsafeChainId 3, upgrade MN3.transactions) + , (unsafeChainId 4, upgrade MN4.transactions) + , (unsafeChainId 5, upgrade MN5.transactions) + , (unsafeChainId 6, upgrade MN6.transactions) + , (unsafeChainId 7, upgrade MN7.transactions) + , (unsafeChainId 8, upgrade MN8.transactions) + , (unsafeChainId 9, upgrade MN9.transactions) + ]) + , (Pact4Coin3, AllChains (Upgrade CoinV3.transactions True)) + , (Chainweb214Pact, AllChains (Upgrade CoinV4.transactions True)) + , (Chainweb215Pact, AllChains (Upgrade CoinV5.transactions True)) + ]) + (onChains [(unsafeChainId 0, HM.singleton to20ChainsTestnet (upgrade MNKAD.transactions))]) + , _versionCheats = Cheats + { _disablePeerValidation = False + , _disablePow = False + , _disableMempool = False + , _disablePact = False + } + } diff --git a/src/Chainweb/Version/Utils.hs b/src/Chainweb/Version/Utils.hs index df552a1079..2682049e60 100644 --- a/src/Chainweb/Version/Utils.hs +++ b/src/Chainweb/Version/Utils.hs @@ -1,5 +1,7 @@ {-# LANGUAGE BangPatterns #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -55,6 +57,9 @@ module Chainweb.Version.Utils ) where import Chainweb.BlockHeight +import Chainweb.BlockHeader +import Chainweb.ChainId +import Chainweb.Difficulty import Chainweb.Time import Data.Foldable @@ -71,6 +76,7 @@ import System.Random import Chainweb.Graph import Chainweb.Utils +import Chainweb.Utils.Rule import Chainweb.Version -- -------------------------------------------------------------------------- -- @@ -110,29 +116,7 @@ atCutHeight h = snd . fromJuste . M.lookupLE h -- @ -- chainGraphs :: HasChainwebVersion v => v -> M.Map BlockHeight ChainGraph -chainGraphs v = case _chainwebVersion v of - Mainnet01 -> mainnet01GraphMap - Testnet04 -> testnet04GraphMap - Development -> developmentGraphMap - x -> M.fromList . toList $ chainwebGraphs x - --- | Memoized mainnet01 chainweb graphs map --- -mainnet01GraphMap :: M.Map BlockHeight ChainGraph -mainnet01GraphMap = M.fromList . toList $ chainwebGraphs Mainnet01 -{-# NOINLINE mainnet01GraphMap #-} - --- | Memoized testnet04 chainweb graphs map --- -testnet04GraphMap :: M.Map BlockHeight ChainGraph -testnet04GraphMap = M.fromList . toList $ chainwebGraphs Testnet04 -{-# NOINLINE testnet04GraphMap #-} - --- | Memoized devnet chainweb graphs map --- -developmentGraphMap :: M.Map BlockHeight ChainGraph -developmentGraphMap = M.fromList . toList $ chainwebGraphs Development -{-# NOINLINE developmentGraphMap #-} +chainGraphs v = M.fromDistinctDescList . toList . ruleElems minBound $ _versionGraphs $ _chainwebVersion v -- | BlockHeight intervals for the chain graphs of a chainweb version up to a -- given block height. @@ -281,7 +265,7 @@ globalBlockRateAt => v -> BlockHeight -> Double -globalBlockRateAt v h = int r / int (chainCountAt v h) +globalBlockRateAt v h = (int r / 1_000_000) / int (chainCountAt v h) where BlockRate r = blockRate (_chainwebVersion v) @@ -310,14 +294,12 @@ chainGraphsByCutHeight :: HasChainwebVersion v => v -> M.Map CutHeight ChainGraph -chainGraphsByCutHeight = M.fromAscList +chainGraphsByCutHeight = M.fromList . fmap (\(h,g) -> (int h * int (order g), g)) . M.toAscList . chainGraphs {-# INLINE chainGraphsByCutHeight #-} --- chainGraphAtCutHeight = atCutHeight h - -- | The chain graph at the given cut height -- -- Note, that the result isn't accurate during a chain graph change @@ -410,7 +392,7 @@ expectedBlockCountAfterSeconds -> cid -> Seconds -> Double -expectedBlockCountAfterSeconds v cid s = max 0 (1 + (int s / int r) - int gh) +expectedBlockCountAfterSeconds v cid s = max 0 (1 + (int s / (int r / 1_000_000)) - int gh) -- The `max 0` term is required for chains that were added during graph transitions -- and thus have `genesisHeight > 0` where @@ -448,7 +430,7 @@ expectedBlockHeightAfterSeconds => v -> Seconds -> Double -expectedBlockHeightAfterSeconds v s = (int s / int r) +expectedBlockHeightAfterSeconds v s = int s / (int r / 1_000_000) where BlockRate r = blockRate (_chainwebVersion v) diff --git a/src/Chainweb/WebBlockHeaderDB.hs b/src/Chainweb/WebBlockHeaderDB.hs index af067704a4..337e8478b2 100644 --- a/src/Chainweb/WebBlockHeaderDB.hs +++ b/src/Chainweb/WebBlockHeaderDB.hs @@ -48,6 +48,7 @@ import Data.Functor.Of import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.List as L +import GHC.Stack import qualified Streaming.Prelude as S @@ -56,7 +57,6 @@ import qualified Streaming.Prelude as S import Chainweb.BlockHeight import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) import Chainweb.BlockHeader.Validation import Chainweb.BlockHeaderDB import Chainweb.BlockHeaderDB.Internal @@ -123,7 +123,7 @@ instance (k ~ CasKeyType (ChainValue BlockHeader)) => ReadableTable WebBlockHead {-# INLINE tableLookup #-} initWebBlockHeaderDb - :: RocksDb + :: HasCallStack => RocksDb -> ChainwebVersion -> IO WebBlockHeaderDb initWebBlockHeaderDb db v = WebBlockHeaderDb diff --git a/src/Chainweb/WebPactExecutionService.hs b/src/Chainweb/WebPactExecutionService.hs index 7a59f6a6f4..e353e73c1e 100644 --- a/src/Chainweb/WebPactExecutionService.hs +++ b/src/Chainweb/WebPactExecutionService.hs @@ -26,7 +26,6 @@ import GHC.Stack import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (emptyPayload) import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Mempool.Mempool (InsertError) @@ -36,6 +35,7 @@ import Chainweb.Pact.Service.PactQueue import Chainweb.Pact.Service.Types import Chainweb.Payload import Chainweb.Transaction +import Chainweb.Version import Chainweb.Utils (T2) import Pact.Types.Command diff --git a/src/P2P/Node.hs b/src/P2P/Node.hs index a1aa696f54..f3040c5448 100644 --- a/src/P2P/Node.hs +++ b/src/P2P/Node.hs @@ -10,6 +10,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} -- | -- Module: P2P.Node @@ -384,10 +385,7 @@ guardPeerDb v nid peerDb pinf = do else return $ Left $ NodeVersionNotAccepted pinf nodeVersion where isReserved :: Bool - isReserved = case v of - Mainnet01 -> isReservedHostAddress (_peerAddr pinf) - Testnet04 -> isReservedHostAddress (_peerAddr pinf) - _ -> False + isReserved = not (v ^. versionCheats . disablePeerValidation) && isReservedHostAddress (_peerAddr pinf) -- Currently we are using 'getNewPeerManager' which doesn't validate -- certificates. We could be more strict and check that the certificate @@ -395,7 +393,7 @@ guardPeerDb v nid peerDb pinf = do -- canConnect = do mgr <- getNewPeerManager - getNodeVersion mgr v (_peerAddr pinf) (Just $ networkIdToText nid <> "/peer") + getNodeVersion mgr (_versionName v) (_peerAddr pinf) (Just $ networkIdToText nid <> "/peer") -- Only compare the address because even for equal peer infos the peer -- ID may be 'Nothing' for one peer and 'Just' some value for the other. @@ -719,11 +717,12 @@ waitAnySession node = do -- | Start a 'PeerDb' for the given set of NetworkIds -- startPeerDb - :: HS.HashSet NetworkId + :: ChainwebVersion + -> HS.HashSet NetworkId -> P2pConfiguration -> IO PeerDb -startPeerDb nids conf = do - !peerDb <- newEmptyPeerDb +startPeerDb v nids conf = do + !peerDb <- newEmptyPeerDb v forM_ nids $ \nid -> peerDbInsertPeerInfoList_ True nid (_p2pConfigKnownPeers conf) peerDb return $ if _p2pConfigPrivate conf @@ -739,11 +738,12 @@ stopPeerDb _ _ = return () -- | Run a computation with a PeerDb -- withPeerDb - :: HS.HashSet NetworkId + :: ChainwebVersion + -> HS.HashSet NetworkId -> P2pConfiguration -> (PeerDb -> IO a) -> IO a -withPeerDb nids conf = bracket (startPeerDb nids conf) (stopPeerDb conf) +withPeerDb v nids conf = bracket (startPeerDb v nids conf) (stopPeerDb conf) -- -------------------------------------------------------------------------- -- -- Create diff --git a/src/P2P/Node/PeerDB.hs b/src/P2P/Node/PeerDB.hs index 79f978a15d..6ad8a83575 100644 --- a/src/P2P/Node/PeerDB.hs +++ b/src/P2P/Node/PeerDB.hs @@ -57,8 +57,6 @@ module P2P.Node.PeerDB , newEmptyPeerDb , makePeerDbPrivate , peerDbSetLocalPeer -, fromPeerEntryList -, fromPeerInfoList , prunePeerDb -- * PeerSet @@ -297,31 +295,35 @@ insertPeerEntryList l m = foldl' (flip addPeerEntry) m l data PeerDb = PeerDb { _peerDbIsPrivate :: !Bool + , _peerDbChainwebVersion :: !ChainwebVersion , _peerDbLocalPeer :: !(Maybe PeerInfo) , _peerDbLock :: !(MVar ()) , _peerDbPeerSet :: !(TVar PeerSet) } deriving (Eq, Generic) +instance HasChainwebVersion PeerDb where + _chainwebVersion = _peerDbChainwebVersion + peerDbSetLocalPeer :: PeerInfo -> PeerDb -> IO PeerDb peerDbSetLocalPeer pinfo db = do peerDbDelete_ db True {- force deletion of sticky peers -} pinfo return db { _peerDbLocalPeer = Just pinfo } peerDbSnapshot :: PeerDb -> IO PeerSet -peerDbSnapshot (PeerDb _ _ _ var) = readTVarIO var +peerDbSnapshot (PeerDb _ _ _ _ var) = readTVarIO var {-# INLINE peerDbSnapshot #-} peerDbSnapshotSTM :: PeerDb -> STM PeerSet -peerDbSnapshotSTM (PeerDb _ _ _ var) = readTVar var +peerDbSnapshotSTM (PeerDb _ _ _ _ var) = readTVar var {-# INLINE peerDbSnapshotSTM #-} peerDbSize :: PeerDb -> IO Natural -peerDbSize (PeerDb _ _ _ var) = int . size <$!> readTVarIO var +peerDbSize (PeerDb _ _ _ _ var) = int . size <$!> readTVarIO var {-# INLINE peerDbSize #-} peerDbSizeSTM :: PeerDb -> STM Natural -peerDbSizeSTM (PeerDb _ _ _ var) = int . size <$!> readTVar var +peerDbSizeSTM (PeerDb _ _ _ _ var) = int . size <$!> readTVar var {-# INLINE peerDbSizeSTM #-} -- | Adds new 'PeerInfo' values for a given chain id. @@ -331,8 +333,8 @@ peerDbSizeSTM (PeerDb _ _ _ var) = int . size <$!> readTVar var -- contention. -- peerDbInsert :: PeerDb -> NetworkId -> PeerInfo -> IO () -peerDbInsert (PeerDb True _ _ _) _ _ = return () -peerDbInsert (PeerDb _ _ lock var) nid i = do +peerDbInsert (PeerDb True _ _ _ _) _ _ = return () +peerDbInsert (PeerDb _ _ _ lock var) nid i = do now <- getCurrentTime withMVar lock . const @@ -344,7 +346,7 @@ peerDbInsert (PeerDb _ _ lock var) nid i = do -- | Delete a peer, identified by its host address, from the peer database. -- peerDbDelete :: PeerDb -> PeerInfo -> IO () -peerDbDelete (PeerDb _ _ lock var) i = withMVar lock +peerDbDelete (PeerDb _ _ _ lock var) i = withMVar lock . const . atomically . modifyTVar' var @@ -357,7 +359,7 @@ peerDbDelete_ -- ^ whether to force deletion of sticky peers (e.g. bootstrap peers) -> PeerInfo -> IO () -peerDbDelete_ (PeerDb _ _ lock var) forceSticky i = withMVar lock +peerDbDelete_ (PeerDb _ _ _ lock var) forceSticky i = withMVar lock . const . atomically . modifyTVar' var @@ -370,7 +372,7 @@ peerDbDelete_ (PeerDb _ _ lock var) forceSticky i = withMVar lock -- 3. have had more than 5 failed connection attempts. -- prunePeerDb :: PeerDb -> IO () -prunePeerDb (PeerDb _ _ lock var) = do +prunePeerDb (PeerDb _ _ _ lock var) = do withMVar lock $ \_ -> do now <- getCurrentTime let cutoff = Just $ addUTCTime ((-60) * 60 * 12) now @@ -383,17 +385,9 @@ prunePeerDb (PeerDb _ _ lock var) = do ||| fromList (filter _peerEntrySticky $ toList s) -fromPeerEntryList :: [PeerEntry] -> IO PeerDb -fromPeerEntryList peers = PeerDb False Nothing - <$> newMVar () - <*> newTVarIO (fromList peers) - -fromPeerInfoList :: NetworkId -> [PeerInfo] -> IO PeerDb -fromPeerInfoList nid peers = fromPeerEntryList $ newPeerEntry nid <$> peers - peerDbInsertList :: [PeerEntry] -> PeerDb -> IO () -peerDbInsertList _ (PeerDb True _ _ _) = return () -peerDbInsertList peers (PeerDb _ _ lock var) = +peerDbInsertList _ (PeerDb True _ _ _ _) = return () +peerDbInsertList peers (PeerDb _ _ _ lock var) = withMVar lock . const . atomically @@ -401,7 +395,7 @@ peerDbInsertList peers (PeerDb _ _ lock var) = $ insertPeerEntryList peers peerDbInsertPeerInfoList :: NetworkId -> [PeerInfo] -> PeerDb -> IO () -peerDbInsertPeerInfoList _ _ (PeerDb True _ _ _) = return () +peerDbInsertPeerInfoList _ _ (PeerDb True _ _ _ _) = return () peerDbInsertPeerInfoList nid ps db = do now <- getCurrentTime peerDbInsertList (mkEntry now <$> ps) db @@ -410,21 +404,21 @@ peerDbInsertPeerInfoList nid ps db = do & set peerEntryLastSuccess (LastSuccess (Just now)) peerDbInsertPeerInfoList_ :: Bool -> NetworkId -> [PeerInfo] -> PeerDb -> IO () -peerDbInsertPeerInfoList_ _ _ _ (PeerDb True _ _ _) = return () +peerDbInsertPeerInfoList_ _ _ _ (PeerDb True _ _ _ _) = return () peerDbInsertPeerInfoList_ sticky nid ps db = peerDbInsertList (newPeerEntry_ sticky nid <$> ps) db peerDbInsertSet :: S.Set PeerEntry -> PeerDb -> IO () -peerDbInsertSet _ (PeerDb True _ _ _) = return () +peerDbInsertSet _ (PeerDb True _ _ _ _) = return () peerDbInsertSet s db = peerDbInsertList (F.toList s) db -newEmptyPeerDb :: IO PeerDb -newEmptyPeerDb = PeerDb False Nothing <$> newMVar () <*> newTVarIO mempty +newEmptyPeerDb :: ChainwebVersion -> IO PeerDb +newEmptyPeerDb v = PeerDb False v Nothing <$> newMVar () <*> newTVarIO mempty makePeerDbPrivate :: PeerDb -> PeerDb -makePeerDbPrivate (PeerDb _ localPeer lock var) = PeerDb True localPeer lock var +makePeerDbPrivate (PeerDb _ v localPeer lock var) = PeerDb True v localPeer lock var updatePeerDb :: PeerDb -> HostAddress -> (PeerEntry -> PeerEntry) -> IO () -updatePeerDb (PeerDb _ _ lock var) a f +updatePeerDb (PeerDb _ _ _ lock var) a f = withMVar lock . const . atomically . modifyTVar' var $ \s -> case getOne $ getEQ a s of Nothing -> s diff --git a/src/P2P/Node/RestAPI/Server.hs b/src/P2P/Node/RestAPI/Server.hs index 070e127b00..411632035a 100644 --- a/src/P2P/Node/RestAPI/Server.hs +++ b/src/P2P/Node/RestAPI/Server.hs @@ -136,7 +136,7 @@ p2pServer (PeerDbT db) = case sing @_ @n of -> peerGetHandler db (MempoolNetwork $ FromSing cid) :<|> peerPutHandler db v (MempoolNetwork $ FromSing cid) where - v = FromSing (SChainwebVersion :: Sing v) + v = _chainwebVersion db -- -------------------------------------------------------------------------- -- -- Application for a single P2P Network diff --git a/src/P2P/Peer.hs b/src/P2P/Peer.hs index a671a64645..6a9181b201 100644 --- a/src/P2P/Peer.hs +++ b/src/P2P/Peer.hs @@ -68,9 +68,6 @@ module P2P.Peer , unsafeCreatePeer , getPeerCertificate --- * Bootstrap Peer Infos -, bootstrapPeerInfos - ) where import Configuration.Utils hiding (Lens') @@ -104,12 +101,9 @@ import Servant.Client import Chainweb.HostAddress import Chainweb.Utils hiding (check) -import Chainweb.Version import Network.X509.SelfSigned -import P2P.BootstrapNodes - -- -------------------------------------------------------------------------- -- -- Peer Id @@ -476,47 +470,3 @@ instance FromJSON Peer where <*> o .: "key" {-# INLINE parseJSON #-} --- -------------------------------------------------------------------------- -- --- Bootstrap Peer Info - --- | For each chainweb version there is a hardcoded set of bootstrap nodes for --- the P2P network. --- --- If a bootstrap node has an public DNS name with an official TLS certificate --- the peer-id should be omitted. For bootstrap nodes without an proper --- certificate, the peer id is the SHA256 hash of the X509 certificate. --- -bootstrapPeerInfos :: ChainwebVersion -> [PeerInfo] -bootstrapPeerInfos Test{} = [testBootstrapPeerInfos] -bootstrapPeerInfos TimedConsensus{} = [testBootstrapPeerInfos] -bootstrapPeerInfos PowConsensus{} = [testBootstrapPeerInfos] -bootstrapPeerInfos TimedCPM{} = [testBootstrapPeerInfos] -bootstrapPeerInfos FastTimedCPM{} = [testBootstrapPeerInfos] -bootstrapPeerInfos Development = [] -bootstrapPeerInfos Testnet04 = domainAddr2PeerInfo testnetBootstrapHosts -bootstrapPeerInfos Mainnet01 = domainAddr2PeerInfo mainnetBootstrapHosts - -testBootstrapPeerInfos :: PeerInfo -testBootstrapPeerInfos = - PeerInfo -#if WITH_ED25519 - { _peerId = Just $ unsafeFromText "BMe2hSdSEGCzLwvoYXPuB1BqYEH5wiV5AvacutSGWmg" -#else - { _peerId = Just $ unsafeFromText "9LkpIG95q5cs0YJg0d-xdR2YLeW_puv1PjS2kEfmEuQ" -#endif - -- this is the fingerprint of the certificate and key that is stored - -- in ./scripts/test-bootstrap-node.config". For programatic use of - -- the same certificate is also available at - -- "Chainweb.Test.P2P.Peer.BootstrapConfig". It is intended for - -- testing purposes only. - - , _peerAddr = HostAddress - { _hostAddressHost = localhost - , _hostAddressPort = 1789 - } - } - --- | Official testnet bootstrap nodes --- -domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] -domainAddr2PeerInfo = fmap (PeerInfo Nothing) diff --git a/test/Chainweb/Test/BlockHeader/Genesis.hs b/test/Chainweb/Test/BlockHeader/Genesis.hs index 001049afa7..c9bdf8b3b4 100644 --- a/test/Chainweb/Test/BlockHeader/Genesis.hs +++ b/test/Chainweb/Test/BlockHeader/Genesis.hs @@ -12,6 +12,8 @@ module Chainweb.Test.BlockHeader.Genesis ( tests ) where +import Control.Lens + import qualified Data.ByteString.Base64.URL as B64U import qualified Data.ByteString.Builder as BB import qualified Data.ByteString.Lazy as BL @@ -26,14 +28,15 @@ import Test.Tasty.QuickCheck (testProperty, testProperties) -- internal modules import Chainweb.BlockHash (encodeBlockHash) -import Chainweb.BlockHeader (BlockHeader(..)) -import Chainweb.BlockHeader.Genesis -import Chainweb.ChainId (ChainId, unsafeChainId) +import Chainweb.BlockHeader hiding (blockHash) import Chainweb.Difficulty import Chainweb.Test.Utils (golden) import Chainweb.Utils import Chainweb.Utils.Serialization -import Chainweb.Version (ChainwebVersion(..)) +import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet --- @@ -67,33 +70,37 @@ graphTransitionTargetTests :: TestTree graphTransitionTargetTests = testGroup "graph transition genesis targets" -- mainnet20InitialHashTarget properties [ testProperty "mainnet20InitialHashTarget deserialization" $ - Just mainnet20InitialHashTarget === (HashTarget . (4 *) <$> decodePowHashNat64 "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") + Just (Mainnet01 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) === (HashTarget . (4 *) <$> decodePowHashNat64 "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") , testProperty "mainnet20InitialHashTarget json deserialization" $ - Just mainnet20InitialHashTarget === (HashTarget . (4 *) <$> decodePowHashNatJson "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") + Just (Mainnet01 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) === (HashTarget . (4 *) <$> decodePowHashNatJson "DOordl9cgfs4ZTBdFnbjRW5th-hW-pL33DIAAAAAAAA") , testProperties "mainnet old chains" $ forChain Mainnet01 maxTarget . unsafeChainId <$> [0..9] , testProperties "mainnet new chains" $ - forChain Mainnet01 mainnet20InitialHashTarget . unsafeChainId <$> [10..19] + forChain Mainnet01 (Mainnet01 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) . unsafeChainId <$> [10..19] -- testnet20InitialHashTarget properties , testProperty "testnet20InitialHashTarget deserialization" $ - Just testnet20InitialHashTarget === (HashTarget <$> decodePowHashNat64 "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") + Just (Testnet04 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) === (HashTarget <$> decodePowHashNat64 "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") , testProperty "testnet20InitialHashTarget json deserialization" $ - Just testnet20InitialHashTarget === (HashTarget <$> decodePowHashNatJson "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") + Just (Testnet04 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) === (HashTarget <$> decodePowHashNatJson "NZIklpW6xujSPrX3gyhXInfxxOS6JDjkW_GbGwAAAAA") , testProperties "testnet old chains" $ forChain Testnet04 maxTarget . unsafeChainId <$> [0..9] , testProperties "testnet new chains" $ - forChain Testnet04 testnet20InitialHashTarget . unsafeChainId <$> [10..19] + forChain Testnet04 (Testnet04 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) . unsafeChainId <$> [10..19] -- Cross check targets to ensure that the values are as expected , testProperty "cross check testnet20InitialHashTarget and mainnet20InitialHashTarget" $ - _hashTarget testnet20InitialHashTarget `div` _hashTarget mainnet20InitialHashTarget === PowHashNat 8893 + _hashTarget (Testnet04 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) `div` + _hashTarget (Mainnet01 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) + === PowHashNat 8893 , testProperty "cross check development and testnet20InitialHashTarget" $ - _hashTarget (genesisBlockTarget Development (unsafeChainId 10)) `div` _hashTarget testnet20InitialHashTarget === PowHashNat 20321 + _hashTarget (Development ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) `div` + _hashTarget (Testnet04 ^?! versionGenesis . genesisBlockTarget . onChain (unsafeChainId 10)) + === PowHashNat 20321 ] where - forChain v target cid = (show cid, genesisBlockTarget v cid === target) + forChain v target cid = (show cid, v ^?! versionGenesis . genesisBlockTarget . onChain cid === target) decodePowHashNat64 t = runGetS decodePowHashNat =<< decodeB64UrlNoPaddingText t decodePowHashNatJson t = decodeStrictOrThrow' @_ @PowHashNat $ "\"" <> t <> "\"" diff --git a/test/Chainweb/Test/BlockHeader/Validation.hs b/test/Chainweb/Test/BlockHeader/Validation.hs index d1b165cc32..d3303ed178 100644 --- a/test/Chainweb/Test/BlockHeader/Validation.hs +++ b/test/Chainweb/Test/BlockHeader/Validation.hs @@ -49,10 +49,14 @@ import Chainweb.Difficulty import Chainweb.Graph hiding (AdjacentChainMismatch) import Chainweb.Test.Orphans.Internal () import Chainweb.Test.Utils.TestHeader +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils hiding ((==>)) import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet import Numeric.AffineSpace @@ -66,7 +70,7 @@ tests = testGroup "Chainweb.Test.Blockheader.Validation" , prop_fail_validate , prop_da_validate , prop_legacy_da_validate - , prop_featureFlag (Test petersonChainGraph) 10 + , prop_featureFlag (barebonesTestVersion petersonChainGraph) 10 , testProperty "validate arbitrary test header" prop_validateArbitrary , testProperty "validate arbitrary test header for mainnet" $ prop_validateArbitrary Mainnet01 , testProperty "validate arbitrary test header for testnet" $ prop_validateArbitrary Testnet04 diff --git a/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs b/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs index 3dceb90f0a..72df1179b3 100644 --- a/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs +++ b/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs @@ -45,6 +45,7 @@ import Chainweb.Test.Utils import Chainweb.Test.Utils.BlockHeader import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Development import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB diff --git a/test/Chainweb/Test/Cut.hs b/test/Chainweb/Test/Cut.hs index f26a38f10e..3d7320d944 100644 --- a/test/Chainweb/Test/Cut.hs +++ b/test/Chainweb/Test/Cut.hs @@ -98,6 +98,7 @@ import Chainweb.Cut.Create import Chainweb.Graph import Chainweb.Payload import Chainweb.Test.Utils.BlockHeader +import Chainweb.Test.TestVersions import Chainweb.Time (Micros(..), Time, TimeSpan) import qualified Chainweb.Time as Time (second) import Chainweb.Utils @@ -625,7 +626,7 @@ prop_blockCountAtChainHeight g0 g1 = all p [0..10] h i = min 8 (i + 1) * int (order g0) + max 0 (i - 7) * int (order g1) -- (8, g1) :| [(0, g0)] - v = TimedConsensus g0 g1 + v = timedConsensusVersion g0 g1 properties_misc :: [(String, T.Property)] properties_misc = @@ -654,7 +655,7 @@ properties db <> properties_miscCut db v <> properties_misc where - v = Test pairChainGraph + v = barebonesTestVersion pairChainGraph -- -------------------------------------------------------------------------- -- -- TestTools diff --git a/test/Chainweb/Test/CutDB.hs b/test/Chainweb/Test/CutDB.hs index 1bab665076..0b36b475f0 100644 --- a/test/Chainweb/Test/CutDB.hs +++ b/test/Chainweb/Test/CutDB.hs @@ -60,10 +60,10 @@ import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Cut import Chainweb.Cut.CutHashes +import Chainweb.Graph import Chainweb.Test.Cut import Chainweb.CutDB import Chainweb.CutDB.RestAPI.Server -import Chainweb.Graph import Chainweb.Miner.Pact import Chainweb.Payload import Chainweb.Payload.PayloadStore @@ -71,7 +71,8 @@ import Chainweb.Payload.PayloadStore.RocksDB import Chainweb.Sync.WebBlockHeaderStore import Chainweb.Test.Orphans.Internal () import Chainweb.Test.Sync.WebBlockHeaderStore -import Chainweb.Test.Utils +import Chainweb.Test.Utils hiding (awaitBlockHeight) +import Chainweb.Test.TestVersions (barebonesTestVersion) import Chainweb.Time import Chainweb.TreeDB (MaxRank(..)) import Chainweb.Utils @@ -509,7 +510,7 @@ tests rdb = testGroup "CutDB" testCutPruning :: RocksDb -> TestTree testCutPruning rdb = testCase "cut pruning" $ do -- initialize cut DB and mine enough to trigger pruning - let v = Test pairChainGraph + let v = barebonesTestVersion pairChainGraph withTestCutDbWithoutPact rdb v alterPruningSettings (int $ avgCutHeightAt v minedBlockHeight) (\_ _ -> return ()) @@ -534,7 +535,7 @@ testCutPruning rdb = testCase "cut pruning" $ do testCutGet :: RocksDb -> TestTree testCutGet rdb = testCase "cut get" $ do - let v = Test pairChainGraph + let v = barebonesTestVersion pairChainGraph let bh = BlockHeight 300 let ch = avgCutHeightAt v bh let halfCh = ch `div` 2 diff --git a/test/Chainweb/Test/Mempool/Consensus.hs b/test/Chainweb/Test/Mempool/Consensus.hs index 3c041b00fa..eab69c5e7c 100644 --- a/test/Chainweb/Test/Mempool/Consensus.hs +++ b/test/Chainweb/Test/Mempool/Consensus.hs @@ -49,6 +49,7 @@ import Chainweb.Test.Orphans.Time () import Chainweb.Test.Utils import Chainweb.Test.Utils.BlockHeader import Chainweb.Time +import Chainweb.Version import Chainweb.Storage.Table.RocksDB @@ -349,7 +350,7 @@ header' h = do :+: _chainId h :+: BlockWeight (targetToDifficulty target) + _blockWeight h :+: succ (_blockHeight h) - :+: v + :+: _versionCode v :+: epochStart (ParentHeader h) mempty t' :+: nonce :+: MerkleLogBody mempty diff --git a/test/Chainweb/Test/Mempool/InMem.hs b/test/Chainweb/Test/Mempool/InMem.hs index da35e7e33c..afb6d112f1 100644 --- a/test/Chainweb/Test/Mempool/InMem.hs +++ b/test/Chainweb/Test/Mempool/InMem.hs @@ -13,8 +13,8 @@ import Chainweb.Mempool.InMemTypes (InMemConfig(..)) import Chainweb.Mempool.Mempool import Chainweb.Test.Mempool (InsertCheck, MempoolWithFunc(..)) import qualified Chainweb.Test.Mempool +import Chainweb.Test.TestVersions import Chainweb.Utils (Codec(..)) -import Chainweb.Version (ChainwebVersion(..)) ------------------------------------------------------------------------------ tests :: TestTree @@ -26,7 +26,7 @@ tests = testGroup "Chainweb.Test.Mempool" wf f = do mv <- newMVar (pure . V.map Right) let cfg = InMemConfig txcfg mockBlockGasLimit 0 2048 Right (checkMv mv) (1024 * 10) - InMem.withInMemoryMempool cfg (Test singletonChainGraph) $ f mv + InMem.withInMemoryMempool cfg (barebonesTestVersion singletonChainGraph) $ f mv checkMv :: MVar (t -> IO b) -> t -> IO b checkMv mv xs = do diff --git a/test/Chainweb/Test/Mempool/RestAPI.hs b/test/Chainweb/Test/Mempool/RestAPI.hs index 2eab0a80ab..df0e23f14d 100644 --- a/test/Chainweb/Test/Mempool/RestAPI.hs +++ b/test/Chainweb/Test/Mempool/RestAPI.hs @@ -26,6 +26,7 @@ import Chainweb.RestAPI import Chainweb.Test.Mempool (InsertCheck, MempoolWithFunc(..)) import qualified Chainweb.Test.Mempool import Chainweb.Test.Utils (withTestAppServer) +import Chainweb.Test.TestVersions import Chainweb.Utils (Codec(..)) import Chainweb.Version import Chainweb.Version.Utils @@ -70,12 +71,12 @@ newTestServer = mask_ $ do server inMemCfg inmemMv envMv restore = InMem.withInMemoryMempool inMemCfg version $ \inmem -> do putMVar inmemMv inmem - restore $ withTestAppServer True version (return $! mkApp inmem) mkEnv $ \env -> do + restore $ withTestAppServer True (return $! mkApp inmem) mkEnv $ \env -> do putMVar envMv env atomically retry version :: ChainwebVersion - version = Test singletonChainGraph + version = barebonesTestVersion singletonChainGraph host :: String host = "127.0.0.1" diff --git a/test/Chainweb/Test/Mempool/Sync.hs b/test/Chainweb/Test/Mempool/Sync.hs index a402f91298..5a123429bd 100644 --- a/test/Chainweb/Test/Mempool/Sync.hs +++ b/test/Chainweb/Test/Mempool/Sync.hs @@ -26,8 +26,8 @@ import Chainweb.Mempool.InMemTypes import Chainweb.Mempool.Mempool import Chainweb.Test.Mempool (InsertCheck, MempoolWithFunc(..), lookupIsPending, mempoolProperty) +import Chainweb.Test.TestVersions (barebonesTestVersion) import Chainweb.Utils (Codec(..)) -import Chainweb.Version (ChainwebVersion(..)) ------------------------------------------------------------------------------ @@ -40,7 +40,7 @@ tests = testGroup "Chainweb.Mempool.sync" wf f = do mv <- newMVar (pure . V.map Right) let cfg = InMemConfig txcfg mockBlockGasLimit 0 2048 Right (checkMv mv) (1024 * 10) - withInMemoryMempool cfg (Test singletonChainGraph) $ f mv + withInMemoryMempool cfg (barebonesTestVersion singletonChainGraph) $ f mv checkMv :: MVar (t -> IO b) -> t -> IO b checkMv mv xs = do @@ -75,7 +75,7 @@ propSync -> MempoolBackend MockTx -> IO (Either String ()) propSync (txs, missing, later) _ localMempool' = - withInMemoryMempool testInMemCfg (Test singletonChainGraph) $ \remoteMempool -> do + withInMemoryMempool testInMemCfg (barebonesTestVersion singletonChainGraph) $ \remoteMempool -> do mempoolInsert localMempool' CheckedInsert txsV mempoolInsert remoteMempool CheckedInsert txsV mempoolInsert remoteMempool CheckedInsert missingV diff --git a/test/Chainweb/Test/Mining.hs b/test/Chainweb/Test/Mining.hs index 6908863782..80ce82dbf4 100644 --- a/test/Chainweb/Test/Mining.hs +++ b/test/Chainweb/Test/Mining.hs @@ -43,7 +43,7 @@ import Chainweb.Miner.Config import Chainweb.Miner.Coordinator import Chainweb.Miner.Pact import Chainweb.Test.CutDB hiding (tests) -import Chainweb.Version +import Chainweb.Test.TestVersions (barebonesTestVersion) import Chainweb.Storage.Table.RocksDB @@ -58,7 +58,7 @@ tests rdb = testGroup "Mining" -- -------------------------------------------------------------------------- -- -- Test Mining Coordinator -withTestCoordiantor +withTestCoordinator :: HasCallStack => RocksDb -> Maybe MiningConfig @@ -66,9 +66,10 @@ withTestCoordiantor -- set to enabled before the coordinator is initialized. -> (forall tbl logger . Logger logger => logger -> MiningCoordination logger tbl -> IO ()) -> IO () -withTestCoordiantor rdb maybeConf a = do +withTestCoordinator rdb maybeConf a = do + let v = barebonesTestVersion pairChainGraph var <- newEmptyMVar - x <- race (takeMVar var) $ + x <- race (takeMVar var) $ withTestCutDb rdb v id 0 (\_ _ -> return fakePact) (logFunction logger) $ \_ cdb -> withMiningCoordination logger conf cdb $ \case Nothing -> error "nonEmptyMiningAccount: Bug in the mining Code" @@ -76,11 +77,10 @@ withTestCoordiantor rdb maybeConf a = do a logger coord putMVar var () case x of - Left () -> logFunctionText logger Info "withTestCoordiantor: action finished" - Right () -> logFunctionText logger Info "withTestCoordiantor: coordinator service stopped" + Left () -> logFunctionText logger Info "withTestCoordinator: action finished" + Right () -> logFunctionText logger Info "withTestCoordinator: coordinator service stopped" where - v = Test pairChainGraph logger = genericLogger Warn print conf = fromMaybe defaultMining maybeConf & miningCoordination . coordinationEnabled .~ True @@ -89,7 +89,7 @@ withTestCoordiantor rdb maybeConf a = do -- Tests nonEmptyMiningAccount :: HasCallStack => RocksDb -> Assertion -nonEmptyMiningAccount rdb = withTestCoordiantor rdb Nothing $ \_logger coord -> do +nonEmptyMiningAccount rdb = withTestCoordinator rdb Nothing $ \_logger coord -> do PrimedWork w <- readTVarIO (_coordPrimedWork coord) forM_ (HM.keys w) $ \(MinerId k) -> assertBool "miner account name must not be the empty string" (not (T.null k)) diff --git a/test/Chainweb/Test/MultiNode.hs b/test/Chainweb/Test/MultiNode.hs index 4d916fc175..5237accd6e 100644 --- a/test/Chainweb/Test/MultiNode.hs +++ b/test/Chainweb/Test/MultiNode.hs @@ -182,7 +182,7 @@ multiBootstrapConfig conf = conf & set (configP2p . p2pConfigPeer) peerConfig & set (configP2p . p2pConfigKnownPeers) [] where - peerConfig = (head $ bootstrapPeerConfig $ _configChainwebVersion conf) + peerConfig = (head $ testBootstrapPeerConfig $ _configChainwebVersion conf) & set peerConfigPort 0 -- Normally, the port of bootstrap nodes is hard-coded. But in -- test-suites that may run concurrently we want to use a port that is @@ -298,8 +298,8 @@ replayTest -> ChainwebVersion -> Natural -> TestTree -replayTest loglevel v n = after AllFinish "ConsensusNetwork" $ testCaseSteps name $ \step -> - withTempRocksDb "replay-test-rocks" $ \rdb -> +replayTest loglevel v n = after AllFinish "ConsensusNetwork" $ testCaseSteps name $ \step -> + withTempRocksDb "replay-test-rocks" $ \rdb -> withSystemTempDirectory "replay-test-pact" $ \pactDbDir -> do let tastylog = step . T.unpack tastylog "phase 1..." @@ -364,9 +364,9 @@ test -> Natural -> Seconds -> TestTree -test loglevel v n seconds = testCaseSteps name $ \f -> +test loglevel v n seconds = testCaseSteps name $ \f -> -- Count log messages and only print the first 60 messages - withTempRocksDb "multinode-tests" $ \rdb -> + withTempRocksDb "multinode-tests" $ \rdb -> withSystemTempDirectory "replay-test-pact" $ \pactDbDir -> do let tastylog = f . T.unpack #if DEBUG_MULTINODE_TEST diff --git a/test/Chainweb/Test/Orphans/Internal.hs b/test/Chainweb/Test/Orphans/Internal.hs index 96eb6467cf..fa3ba27d6c 100644 --- a/test/Chainweb/Test/Orphans/Internal.hs +++ b/test/Chainweb/Test/Orphans/Internal.hs @@ -145,6 +145,9 @@ import Chainweb.Utils import Chainweb.Utils.Paging import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet import Chainweb.Version.Utils import Data.Singletons @@ -178,27 +181,27 @@ instance Arbitrary Utf8Encoded where -- -------------------------------------------------------------------------- -- -- Basics --- FIXME: This doesn't throw pattern-match warnings when a new `ChainwebVersion` --- constructor is invented! instance Arbitrary ChainwebVersion where arbitrary = elements - [ Test singletonChainGraph - , Test petersonChainGraph - , TimedConsensus singletonChainGraph singletonChainGraph - , TimedConsensus petersonChainGraph petersonChainGraph - , TimedConsensus singletonChainGraph pairChainGraph - , TimedConsensus petersonChainGraph twentyChainGraph - , PowConsensus singletonChainGraph - , PowConsensus petersonChainGraph - , TimedCPM singletonChainGraph - , TimedCPM petersonChainGraph - , FastTimedCPM singletonChainGraph - , FastTimedCPM petersonChainGraph - , Development + -- [ Test singletonChainGraph + -- , Test petersonChainGraph + -- , TimedConsensus singletonChainGraph singletonChainGraph + -- , TimedConsensus petersonChainGraph petersonChainGraph + -- , TimedConsensus singletonChainGraph pairChainGraph + -- , TimedConsensus petersonChainGraph twentyChainGraph + -- , PowConsensus singletonChainGraph + -- , PowConsensus petersonChainGraph + [ Development , Testnet04 , Mainnet01 ] +instance Arbitrary ChainwebVersionName where + arbitrary = _versionName <$> arbitrary + +instance Arbitrary ChainwebVersionCode where + arbitrary = _versionCode <$> arbitrary + instance MerkleHashAlgorithm a => Arbitrary (MerkleLogHash a) where arbitrary = unsafeMerkleLogHash . B.pack <$> vector (int merkleLogHashBytesCount) @@ -283,11 +286,12 @@ instance Arbitrary NodeInfo where curGraph = head $ dropWhile (\(h,_) -> h > curHeight) graphs curChains = map fst $ snd curGraph return $ NodeInfo - { nodeVersion = v + { nodeVersion = _versionName v , nodeApiVersion = prettyApiVersion , nodeChains = T.pack . show <$> curChains , nodeNumberOfChains = length curChains , nodeGraphHistory = graphs + , nodeLatestBehaviorHeight = latestBehaviorAt v } -- -------------------------------------------------------------------------- -- @@ -372,7 +376,7 @@ arbitraryBlockHeaderVersionHeightChain v h cid $ liftA2 (:+:) (pure cid) -- chain id $ liftA2 (:+:) arbitrary -- weight $ liftA2 (:+:) (pure h) -- height - $ liftA2 (:+:) (pure v) -- version + $ liftA2 (:+:) (pure (_versionCode v)) -- version $ liftA2 (:+:) (EpochStartTime <$> chooseEnum (toEnum 0, t)) -- epoch start $ liftA2 (:+:) (Nonce <$> chooseAny) -- nonce $ fmap (MerkleLogBody . blockHashRecordToVector) @@ -943,9 +947,6 @@ instance Arbitrary GasLimit where instance Arbitrary GasPrice where arbitrary = GasPrice <$> (getPositive <$> arbitrary) -instance Arbitrary MockTx where - arbitrary = MockTx <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary - instance Arbitrary t => Arbitrary (ValidatedTransaction t) where arbitrary = ValidatedTransaction <$> arbitrary <*> arbitrary <*> arbitrary diff --git a/test/Chainweb/Test/P2P/Peer/BootstrapConfig.hs b/test/Chainweb/Test/P2P/Peer/BootstrapConfig.hs index ff2f7be17a..ed39976355 100644 --- a/test/Chainweb/Test/P2P/Peer/BootstrapConfig.hs +++ b/test/Chainweb/Test/P2P/Peer/BootstrapConfig.hs @@ -13,9 +13,9 @@ module Chainweb.Test.P2P.Peer.BootstrapConfig ( -- * Bootstrap Peer Config - bootstrapPeerConfig -, bootstrapCertificate -, bootstrapKey + testBootstrapPeerConfig +, testBootstrapCertificate +, testBootstrapKey ) where import qualified Data.ByteString.Char8 as B8 @@ -33,27 +33,15 @@ import P2P.Peer -- | Peer configuration for bootstrap hard-coded bootstrap peer infos. -- -bootstrapPeerConfig :: ChainwebVersion -> [PeerConfig] -bootstrapPeerConfig v@Test{} = testBootstrapPeerConfig v -bootstrapPeerConfig v@TimedConsensus{} = testBootstrapPeerConfig v -bootstrapPeerConfig v@PowConsensus{} = testBootstrapPeerConfig v -bootstrapPeerConfig v@TimedCPM{} = testBootstrapPeerConfig v -bootstrapPeerConfig v@FastTimedCPM{} = testBootstrapPeerConfig v -bootstrapPeerConfig Development = error - $ "bootstrap peer config isn't defined for chainweb version Development" -bootstrapPeerConfig Testnet04 = error - $ "bootstrap peer config isn't defined for chainweb version Testnet04" -bootstrapPeerConfig Mainnet01 = error - $ "bootstrap peer config isn't defined for chainweb version Testnet04" testBootstrapPeerConfig :: ChainwebVersion -> [PeerConfig] testBootstrapPeerConfig v = [ PeerConfig - { _peerConfigAddr = _peerAddr $ head (bootstrapPeerInfos v) + { _peerConfigAddr = _peerAddr $ head (_versionBootstraps v) , _peerConfigInterface = "127.0.0.1" - , _peerConfigCertificateChain = Just $ X509CertChainPem (bootstrapCertificate v) [] + , _peerConfigCertificateChain = Just $ X509CertChainPem testBootstrapCertificate [] , _peerConfigCertificateChainFile = Nothing - , _peerConfigKey = Just $ bootstrapKey v + , _peerConfigKey = Just testBootstrapKey , _peerConfigKeyFile = Nothing } ] @@ -65,19 +53,6 @@ testBootstrapPeerConfig v = -- Public Chainweb versions should rely on public DNS names with official TLS -- certificates for bootstrapping. -- -bootstrapCertificate :: ChainwebVersion -> X509CertPem -bootstrapCertificate Test{} = testBootstrapCertificate -bootstrapCertificate TimedConsensus{} = testBootstrapCertificate -bootstrapCertificate PowConsensus{} = testBootstrapCertificate -bootstrapCertificate TimedCPM{} = testBootstrapCertificate -bootstrapCertificate FastTimedCPM{} = testBootstrapCertificate -bootstrapCertificate Development = error - $ "bootstrap certificate isn't defined for chainweb version Development" -bootstrapCertificate Testnet04 = error - $ "bootstrap certificate isn't defined for chainweb version Testnet04" -bootstrapCertificate Mainnet01 = error - $ "bootstrap certificate isn't defined for chainweb version Mainnet01" - -- | The test certificate is also stored in the file -- @./scripts/scripts/test-bootstrap-node.config@. -- @@ -127,19 +102,6 @@ testBootstrapCertificate = X509CertPem $ B8.intercalate "\n" ] #endif -bootstrapKey :: ChainwebVersion -> X509KeyPem -bootstrapKey Test{} = testBootstrapKey -bootstrapKey TimedConsensus{} = testBootstrapKey -bootstrapKey PowConsensus{} = testBootstrapKey -bootstrapKey TimedCPM{} = testBootstrapKey -bootstrapKey FastTimedCPM{} = testBootstrapKey -bootstrapKey Development = error - $ "bootstrap key isn't defined for chainweb version Development" -bootstrapKey Testnet04 = error - $ "bootstrap key isn't defined for chainweb version Testnet04" -bootstrapKey Mainnet01 = error - $ "bootstrap key isn't defined for chainweb version Mainnet01" - -- | This is only defined for non-public Test instances -- testBootstrapKey :: X509KeyPem diff --git a/test/Chainweb/Test/Pact/Checkpointer.hs b/test/Chainweb/Test/Pact/Checkpointer.hs index cbe1c242df..43740b439a 100644 --- a/test/Chainweb/Test/Pact/Checkpointer.hs +++ b/test/Chainweb/Test/Pact/Checkpointer.hs @@ -40,6 +40,7 @@ import Test.Tasty.HUnit -- internal imports import Chainweb.BlockHash +import Chainweb.BlockHeader import Chainweb.BlockHeight (BlockHeight(..)) import Chainweb.MerkleLogHash (merkleLogHash) import Chainweb.MerkleUniverse @@ -52,6 +53,7 @@ import Chainweb.Pact.TransactionExec import Chainweb.Pact.Types import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Utils (catchAllSynchronous) import Chainweb.Version @@ -549,7 +551,7 @@ runRegression pactdb e schemaInit = do -- Chainweb Settings testVer :: ChainwebVersion -testVer = FastTimedCPM peterson +testVer = slowForkingCpmTestVersion peterson testChainId :: ChainId testChainId = unsafeChainId 0 @@ -608,7 +610,7 @@ runSQLite' runTest sqlEnvIO = runTest $ do runExec :: CheckpointEnv -> PactDbEnv'-> Maybe Value -> Text -> IO EvalResult runExec cp (PactDbEnv' pactdbenv) eData eCode = do - execMsg <- buildExecParsedCode Nothing {- use latest parser version -} eData eCode + execMsg <- buildExecParsedCode maxBound {- use latest parser version -} eData eCode evalTransactionM cmdenv cmdst $ applyExec' 0 defaultInterpreter execMsg [] h' permissiveNamespacePolicy where diff --git a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs index 4b5cd16c3d..feb9675e2c 100644 --- a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs +++ b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs @@ -35,8 +35,8 @@ import Pact.Types.Term -- chainweb imports import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.ChainId +import Chainweb.Graph import Chainweb.Logger import Chainweb.Miner.Pact import Chainweb.Pact.Backend.Types @@ -49,14 +49,15 @@ import Chainweb.Test.Cut import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Utils import Chainweb.Test.Pact.Utils -import Chainweb.Utils (T2(..)) +import Chainweb.Test.TestVersions(fastForkingCpmTestVersion) +import Chainweb.Utils (T2(..), fromJuste) import Chainweb.Version import Chainweb.WebBlockHeaderDB import Chainweb.Storage.Table.RocksDB testVer :: ChainwebVersion -testVer = FastTimedCPM singleton +testVer = fastForkingCpmTestVersion singletonChainGraph testChainId :: ChainId testChainId = unsafeChainId 0 @@ -278,7 +279,7 @@ withPact' bdbio ioSqlEnv r (ps, cacheTest) = do bhdb <- getWebBlockHeaderDb (_bdbWebBlockHeaderDb bdb) testChainId let pdb = _bdbPayloadDb bdb sqlEnv <- ioSqlEnv - T2 _ pstate <- runPactService' + T2 _ pstate <- withPactService testVer testChainId logger bhdb pdb sqlEnv defaultPactServiceConfig ps cacheTest r (_psInitCache pstate) where diff --git a/test/Chainweb/Test/Pact/PactExec.hs b/test/Chainweb/Test/Pact/PactExec.hs index d5c028c6e4..31782f5e61 100644 --- a/test/Chainweb/Test/Pact/PactExec.hs +++ b/test/Chainweb/Test/Pact/PactExec.hs @@ -39,7 +39,7 @@ import Test.Tasty.HUnit -- internal modules -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) +import Chainweb.BlockHeader (genesisBlockHeader) import Chainweb.BlockHeaderDB (BlockHeaderDb) import Chainweb.Graph import Chainweb.Miner.Pact @@ -52,6 +52,7 @@ import Chainweb.Payload.PayloadStore import Chainweb.Payload.PayloadStore.InMemory (newPayloadDb) import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Transaction import Chainweb.Version (ChainwebVersion(..)) import Chainweb.Version.Utils (someChainId) @@ -66,10 +67,10 @@ import Pact.Types.Persistence import Pact.Types.Pretty testVersion :: ChainwebVersion -testVersion = FastTimedCPM petersonChainGraph +testVersion = slowForkingCpmTestVersion petersonChainGraph testEventsVersion :: ChainwebVersion -testEventsVersion = FastTimedCPM singletonChainGraph +testEventsVersion = fastForkingCpmTestVersion singletonChainGraph tests :: ScheduledTest tests = ScheduledTest label $ diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 8fc922bc07..6e5fb66427 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -56,6 +56,7 @@ import Chainweb.Test.Cut import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils import Chainweb.Version @@ -65,7 +66,7 @@ import Chainweb.WebPactExecutionService import Chainweb.Storage.Table (casLookupM) testVersion :: ChainwebVersion -testVersion = FastTimedCPM peterson +testVersion = slowForkingCpmTestVersion peterson cid :: ChainId cid = someChainId testVersion @@ -85,7 +86,8 @@ type PactTestM = ReaderT MultiEnv IO data MempoolInput = MempoolInput { _miBlockFill :: BlockFill - , _miBlockHeader :: BlockHeader } + , _miBlockHeader :: BlockHeader + } newtype MempoolCmdBuilder = MempoolCmdBuilder { _mempoolCmdBuilder :: MempoolInput -> CmdBuilder @@ -98,7 +100,8 @@ newtype MempoolBlock = MempoolBlock -- | Mempool with an ordered list of fillers. newtype PactMempool = PactMempool - { _pactMempool :: [MempoolBlock] } + { _pactMempool :: [MempoolBlock] + } deriving (Semigroup,Monoid) @@ -127,7 +130,7 @@ tests = ScheduledTest testName go where -- This is way more than what is used in production, but during testing -- we can be generous. - generousConfig = defaultPactServiceConfig { _pactBlockGasLimit = 300_000 } + generousConfig = defaultPactServiceConfig { _pactBlockGasLimit = 300_000, _pactLogGas = True } timeoutConfig = defaultPactServiceConfig { _pactBlockGasLimit = 100_000 } test pactConfig gasmodel tname f = withDelegateMempool $ \dmpio -> testCase tname $ @@ -241,7 +244,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(enforce false 'hi)") $ assertTxFailure "Should fail with the error from the enforce" "hi" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce pre-fork evaluates the string with gas" 34 + assertTxGas "Enforce pre-fork evaluates the string with gas" 35 , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions pre-fork gas" 20 , PactTxTest @@ -255,7 +258,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(+ 1 \'clearlyanerror)") $ assertTxFailure "Should replace tx error with empty error" "" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce post fork does not eval the string" (14 + coinTxBuyTransferGas) + assertTxGas "Enforce post fork does not eval the string" (15 + coinTxBuyTransferGas) , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions post-fork change gas" (40 + coinTxBuyTransferGas) , PactTxTest @@ -652,7 +655,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Pre-fork format gas" 21 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Pre-fork try" 18 + assertTxGas "Pre-fork try" 19 , PactTxTest (buildSimpleCmd defineNonNamespacedPreFork) $ assertTxSuccess "Should pass when defining a non-namespaced keyset" @@ -668,7 +671,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Post-fork format gas increase" 48 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Post-fork try should charge a bit more gas" 19 + assertTxGas "Post-fork try should charge a bit more gas" 20 , PactTxTest (buildSimpleCmd defineNonNamespacedPostFork1) $ assertTxFailure "Should fail when defining a non-namespaced keyset post fork" diff --git a/test/Chainweb/Test/Pact/PactReplay.hs b/test/Chainweb/Test/Pact/PactReplay.hs index 933470e4b3..1b315a0996 100644 --- a/test/Chainweb/Test/Pact/PactReplay.hs +++ b/test/Chainweb/Test/Pact/PactReplay.hs @@ -29,8 +29,8 @@ import Test.Tasty.HUnit import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeaderDB.Internal (unsafeInsertBlockHeaderDb) +import Chainweb.Graph import Chainweb.Test.Cut.TestBlockDb import Chainweb.Miner.Pact import Chainweb.Pact.Backend.Types @@ -41,6 +41,7 @@ import Chainweb.Payload import Chainweb.Payload.PayloadStore import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.TreeDB import Chainweb.Utils (sshow, tryAllSynchronous, catchAllSynchronous, T3(..)) @@ -53,7 +54,7 @@ import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB testVer :: ChainwebVersion -testVer = FastTimedCPM peterson +testVer = fastForkingCpmTestVersion petersonChainGraph cid :: ChainId cid = someChainId testVer diff --git a/test/Chainweb/Test/Pact/PactSingleChainTest.hs b/test/Chainweb/Test/Pact/PactSingleChainTest.hs index c6623aa4f3..9b049d473b 100644 --- a/test/Chainweb/Test/Pact/PactSingleChainTest.hs +++ b/test/Chainweb/Test/Pact/PactSingleChainTest.hs @@ -52,8 +52,7 @@ import Pact.Types.RPC import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis -import Chainweb.ChainId +import Chainweb.Graph import Chainweb.Mempool.Mempool import Chainweb.Miner.Pact import Chainweb.Pact.Backend.Types @@ -66,6 +65,7 @@ import Chainweb.Payload import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils import Chainweb.Version @@ -74,7 +74,7 @@ import Chainweb.Version.Utils import Chainweb.Storage.Table.RocksDB testVersion :: ChainwebVersion -testVersion = FastTimedCPM peterson +testVersion = slowForkingCpmTestVersion petersonChainGraph cid :: ChainId cid = someChainId testVersion diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 86a4e7dd97..a1cc045a45 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -7,6 +7,7 @@ {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} -- | -- Module: Chainweb.Test.RemotePactTest @@ -43,6 +44,7 @@ import Data.Decimal import Data.Default (def) import Data.Foldable (toList) import qualified Data.HashMap.Strict as HashMap +import qualified Data.HashSet as HashSet import qualified Data.List as L import qualified Data.List.NonEmpty as NEL import qualified Data.Map.Strict as M @@ -86,6 +88,7 @@ import Chainweb.Pact.Service.Types import Chainweb.Test.Pact.Utils import Chainweb.Test.RestAPI.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils hiding (check) import Chainweb.Version @@ -100,7 +103,7 @@ nNodes :: Natural nNodes = 1 v :: ChainwebVersion -v = FastTimedCPM petersonChainGraph +v = fastForkingCpmTestVersion petersonChainGraph cid :: HasCallStack => ChainId cid = head . toList $ chainIds v @@ -127,7 +130,7 @@ tests rdb = testGroupSch "Chainweb.Test.Pact.RemotePactTest" withTime $ \iot -> testGroup "remote pact tests" [ testCaseSteps "await network" $ \step -> - awaitNetworkHeight step net 20 + awaitBlockHeight v step (_getClientEnv <$> net) (latestBehaviorAt v) , after AllSucceed "await network" $ withRequestKeys iot iomvar net $ responseGolden net , after AllSucceed "remote-golden" $ @@ -160,16 +163,6 @@ tests rdb = testGroupSch "Chainweb.Test.Pact.RemotePactTest" ] ] --- | Network initialization takes some time. Within my ghci session it took --- about 10 seconds. Once initialization is complete even large numbers of empty --- blocks were mined almost instantaneously. --- -awaitNetworkHeight :: (String -> IO ()) -> IO ChainwebNetwork -> CutHeight -> IO () -awaitNetworkHeight step nio h = do - cenv <- _getClientEnv <$> nio - ch <- awaitCutHeight step cenv h - step $ "cut height: " <> sshow (_cutHashesHeight ch) - responseGolden :: IO ChainwebNetwork -> IO RequestKeys -> TestTree responseGolden networkIO rksIO = golden "remote-golden" $ do rks <- rksIO @@ -225,6 +218,7 @@ localContTest iot nio = testCaseSteps "local continuation test" $ \step -> do $ set cbSigners [mkSigner' sender00 []] $ set cbCreationTime t $ set cbNetworkId (Just v) + $ set cbGasLimit 70000 $ mkCmd "nonce-cont-1" $ mkExec' tx @@ -244,7 +238,7 @@ localChainDataTest iot nio = do mv <- newMVar (0 :: Int) SubmitBatch batch <- localTestBatch iot mv let cmd = head $ toList batch - sid <- mkChainId v maxBound (0 :: Int) + sid <- mkChainId v maxBound 0 res <- flip runClientM cenv $ pactLocalApiClient v sid cmd checkCommandResult res where @@ -258,7 +252,7 @@ localChainDataTest iot nio = do let nonce = "nonce" <> sshow nn t <- toTxCreationTime <$> iott kps <- testKeyPairs sender00 Nothing - c <- Pact.mkExec "(chain-data)" A.Null (pm t) kps (Just "fastTimedCPM-peterson") (Just nonce) + c <- Pact.mkExec "(chain-data)" A.Null (pm t) kps (Just "fastfork-CPM-peterson") (Just nonce) pure (succ nn, SubmitBatch (pure c)) where ttl = 2 * 24 * 60 * 60 @@ -277,7 +271,7 @@ pollingBadlistTest :: IO ChainwebNetwork -> TestTree pollingBadlistTest nio = testCase "/poll reports badlisted txs" $ do cenv <- fmap _getServiceClientEnv nio let rks = RequestKeys $ NEL.fromList [pactDeadBeef] - sid <- liftIO $ mkChainId v maxBound (0 :: Int) + sid <- liftIO $ mkChainId v maxBound 0 void $ polling sid cenv rks ExpectPactError -- | Check request key length validation in the /poll endpoints @@ -289,7 +283,7 @@ pollBadKeyTest nio = let tooBig = toRk $ BS.replicate 33 0x3d tooSmall = toRk $ BS.replicate 31 0x3d - sid <- liftIO $ mkChainId v maxBound (0 :: Int) + sid <- liftIO $ mkChainId v maxBound 0 step "RequestKeys of length > 32 fail fast" runClientM (pactPollApiClient v sid (Poll tooBig)) cenv >>= \case @@ -317,7 +311,7 @@ sendValidationTest iot nio = pactSendApiClient v cid batch step "check sending mismatched chain id" - cid0 <- mkChainId v maxBound (0 :: Int) + cid0 <- mkChainId v maxBound 0 batch3 <- testBatch'' "40" iot 20_000 mv gp expectSendFailure "Transaction metadata (chain id, chainweb version) conflicts with this endpoint" $ flip runClientM cenv $ @@ -326,14 +320,14 @@ sendValidationTest iot nio = step "check insufficient gas" batch4 <- testBatch' iot 10_000 mv 10_000_000_000 expectSendFailure - "(enforce (<= amount balance) \\\"...: Failure: Tx Failed: Insufficient funds\"" $ + "Attempt to buy gas failed with: : Failure: Tx Failed: Insufficient funds" $ flip runClientM cenv $ pactSendApiClient v cid batch4 step "check bad sender" batch5 <- mkBadGasTxBatch "(+ 1 2)" "invalid-sender" sender00 Nothing expectSendFailure - "(read coin-table sender): Failure: Tx Failed: read: row not found: invalid-sender" $ + "Attempt to buy gas failed with: : Failure: Tx Failed: read: row not found: invalid-sender" $ flip runClientM cenv $ pactSendApiClient v cid0 batch5 @@ -343,7 +337,7 @@ sendValidationTest iot nio = t <- toTxCreationTime <$> iot let ttl = 2 * 24 * 60 * 60 pm = Pact.PublicMeta (Pact.ChainId "0") senderName 100_000 0.01 ttl t - let cmd (n :: Int) = liftIO $ Pact.mkExec code A.Null pm ks (Just "fastTimedCPM-peterson") (Just $ sshow n) + let cmd (n :: Int) = liftIO $ Pact.mkExec code A.Null pm ks (Just "fastfork-CPM-peterson") (Just $ sshow n) cmds <- mapM cmd (0 NEL.:| [1..5]) return $ SubmitBatch cmds @@ -370,7 +364,7 @@ ethSpvTest iot nio = testCaseSteps "eth spv client tests" $ \step -> do Right x -> return (x :: EthSpvRequest) cenv <- _getServiceClientEnv <$> nio - c <- mkChainId v maxBound (1 :: Int) + c <- mkChainId v maxBound 1 r <- flip runClientM cenv $ do void $ liftIO $ step "ethSpvApiClient: submit eth proof request" @@ -396,7 +390,7 @@ ethSpvTest iot nio = testCaseSteps "eth spv client tests" $ \step -> do ks <- liftIO $ testKeyPairs sender00 Nothing t <- toTxCreationTime <$> iot let pm = Pact.PublicMeta (Pact.ChainId "1") "sender00" 100_000 0.01 ttl t - cmd <- liftIO $ Pact.mkExec txcode (txdata proof) pm ks (Just "fastTimedCPM-peterson") (Just "1") + cmd <- liftIO $ Pact.mkExec txcode (txdata proof) pm ks (Just "fastfork-CPM-peterson") (Just "1") return $ SubmitBatch (pure cmd) txcode = "(verify-spv 'ETH (read-msg))" @@ -407,7 +401,7 @@ spvTest :: IO (Time Micros) -> IO ChainwebNetwork -> TestTree spvTest iot nio = testCaseSteps "spv client tests" $ \step -> do cenv <- fmap _getServiceClientEnv nio batch <- mkTxBatch - sid <- mkChainId v maxBound (1 :: Int) + sid <- mkChainId v maxBound 1 r <- flip runClientM cenv $ do void $ liftIO $ step "sendApiClient: submit batch" @@ -427,11 +421,12 @@ spvTest iot nio = testCaseSteps "spv client tests" $ \step -> do ttl = 2 * 24 * 60 * 60 mkTxBatch = do - ks <- liftIO $ testKeyPairs sender00 Nothing + ks <- liftIO $ testKeyPairs sender00 + (Just [mkGasCap, mkXChainTransferCap "sender00" "sender01" 1.0 "2"]) t <- toTxCreationTime <$> iot let pm = Pact.PublicMeta (Pact.ChainId "1") "sender00" 100_000 0.01 ttl t - cmd1 <- liftIO $ Pact.mkExec txcode txdata pm ks (Just "fastTimedCPM-peterson") (Just "1") - cmd2 <- liftIO $ Pact.mkExec txcode txdata pm ks (Just "fastTimedCPM-peterson") (Just "2") + cmd1 <- liftIO $ Pact.mkExec txcode txdata pm ks (Just "fastfork-CPM-peterson") (Just "1") + cmd2 <- liftIO $ Pact.mkExec txcode txdata pm ks (Just "fastfork-CPM-peterson") (Just "2") return $ SubmitBatch (pure cmd1 <> pure cmd2) txcode = T.unlines @@ -452,23 +447,24 @@ txTooBigGasTest :: IO (Time Micros) -> IO ChainwebNetwork -> TestTree txTooBigGasTest iot nio = testCaseSteps "transaction size gas tests" $ \step -> do cenv <- fmap _getServiceClientEnv nio - let runSend batch expectation = flip runClientM cenv $ do - void $ liftIO $ step "sendApiClient: submit transaction" - rks <- liftIO $ sending sid cenv batch + let + runSend batch expectation = try @IO @PactTestFailure $ do + void $ step "sendApiClient: submit transaction" + rks <- sending sid cenv batch - void $ liftIO $ step "pollApiClient: polling for request key" - (PollResponses resp) <- liftIO $ polling sid cenv rks expectation + void $ step "pollApiClient: polling for request key" + PollResponses resp <- polling sid cenv rks expectation return (HashMap.lookup (NEL.head $ _rkRequestKeys rks) resp) - let runLocal (SubmitBatch cmds) = do + runLocal (SubmitBatch cmds) = do void $ step "localApiClient: submit transaction" local sid cenv (head $ toList cmds) -- batch with big tx and insufficient gas - batch0 <- mkTxBatch txcode0 A.Null 1 + batch0 <- mkTxBatch txcode0 A.Null 1 (Just "0") -- batch to test that gas for tx size discounted from the total gas supply - batch1 <- mkTxBatch txcode1 A.Null 5 + batch1 <- mkTxBatch txcode1 A.Null 5 (Just "1") res0Send <- runSend batch0 ExpectPactError res1Send <- runSend batch1 ExpectPactError @@ -479,8 +475,8 @@ txTooBigGasTest iot nio = testCaseSteps "transaction size gas tests" $ \step -> void $ liftIO $ step "when tx too big, gas pact error thrown" assertEqual "LOCAL: expect gas error for big tx" gasError0 (Just $ resultOf res0Local) case res0Send of - Left e -> assertFailure $ "test failure for big tx with insuffient gas: " <> show e - Right cr -> assertEqual "SEND: expect gas error for big tx" gasError0 (resultOf <$> cr) + Left e -> assertFailure $ "test failure for big tx with insufficient gas: " <> show e + Right cr -> assertEqual "SEND: expect gas error for big tx" gasError0Mem (resultOf <$> cr) let getFailureMsg (Left (Pact.PactError _ _ _ m)) = m getFailureMsg p = pretty $ "Expected failure result, got " ++ show p @@ -492,20 +488,23 @@ txTooBigGasTest iot nio = testCaseSteps "transaction size gas tests" $ \step -> case res1Send of Left e -> assertFailure $ "test failure for discounting initial gas charge: " <> show e Right cr -> assertEqual "SEND: expect gas error after discounting initial gas charge" - (Just gasError1) (getFailureMsg . resultOf <$> cr) + (Just gasError1Mem) (getFailureMsg . resultOf <$> cr) where sid = unsafeChainId 0 gasError0 = Just $ Left $ Pact.PactError Pact.GasError def [] "Tx too big (4), limit 1" + gasError0Mem = Just $ Left $ + Pact.PactError Pact.TxFailure def [] "Transaction is badlisted because it previously failed to validate." gasError1 = "Gas limit (5) exceeded: 6" + gasError1Mem = "Transaction is badlisted because it previously failed to validate." - mkTxBatch code cdata limit = do + mkTxBatch code cdata limit n = do ks <- testKeyPairs sender00 Nothing t <- toTxCreationTime <$> iot let ttl = 2 * 24 * 60 * 60 pm = Pact.PublicMeta (Pact.ChainId "0") "sender00" limit 0.01 ttl t - cmd <- liftIO $ Pact.mkExec code cdata pm ks (Just "fastTimedCPM-peterson") (Just "0") + cmd <- liftIO $ Pact.mkExec code cdata pm ks (Just "fastfork-CPM-peterson") n return $ SubmitBatch (pure cmd) txcode0 = T.concat ["[", T.replicate 10 " 1", "]"] @@ -577,11 +576,11 @@ allocation02KeyPair' = allocationTest :: IO (Time Micros) -> IO ChainwebNetwork -> TestTree allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do - let testCaseStep = void . liftIO . step + let testCaseStep = void . step cenv <- fmap _getServiceClientEnv nio step "positive allocation test: allocation00 release" - p <- flip runClientM cenv $ do + p <- try @IO @PactTestFailure $ do batch0 <- liftIO $ mkSingletonBatch iot allocation00KeyPair tx0 n0 (pm "allocation00") Nothing @@ -600,53 +599,41 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do Left e -> assertFailure $ "test failure: " <> show e Right cr -> assertEqual "00 expect /local allocation balance" accountInfo (resultOf cr) - step "negative allocation test: allocation01 release" - q <- flip runClientM cenv $ do - batch0 <- liftIO - $ mkSingletonBatch iot allocation01KeyPair tx2 n2 (pm "allocation01") Nothing - - testCaseStep "sendApiClient: submit allocation release request" - rks <- liftIO $ sending sid cenv batch0 + batch0 <- mkSingletonBatch iot allocation01KeyPair tx2 n2 (pm "allocation01") Nothing - testCaseStep "pollApiClient: polling for allocation key" - PollResponses r <- liftIO $ polling sid cenv rks ExpectPactError - return $ toList r + testCaseStep "sendApiClient: submit allocation release request" + cr <- local sid cenv (NEL.head $ _sbCmds batch0) - case q of - Right [cr] -> case resultOf cr of - Left e -> assertBool "expect negative allocation test failure" + case resultOf cr of + Left e -> do + assertBool "expect negative allocation test failure" $ T.isInfixOf "Failure: Tx Failed: funds locked" $ sshow e - _ -> assertFailure "unexpected pact result success in negative allocation test" - _ -> assertFailure "unexpected failure in negative allocation test" - + _ -> assertFailure "unexpected pact result success in negative allocation test" step "positive key-rotation test: allocation2" - r <- flip runClientM cenv $ do + r <- try @IO @PactTestFailure $ do - batch0 <- liftIO - $ mkSingletonBatch iot allocation02KeyPair tx3 n3 (pm "allocation02") Nothing + batch0 <- mkSingletonBatch iot allocation02KeyPair tx3 n3 (pm "allocation02") Nothing testCaseStep "senderApiClient: submit keyset rotation request" - rks <- liftIO $ sending sid cenv batch0 + rks <- sending sid cenv batch0 testCaseStep "pollApiClient: polling for successful rotation" - void $ liftIO $ polling sid cenv rks ExpectPactResult + void $ polling sid cenv rks ExpectPactResult testCaseStep "senderApiClient: submit allocation release request" - batch1 <- liftIO - $ mkSingletonBatch iot allocation02KeyPair' tx4 n4 (pm "allocation02") Nothing + batch1 <- mkSingletonBatch iot allocation02KeyPair' tx4 n4 (pm "allocation02") Nothing - rks' <- liftIO $ sending sid cenv batch1 + rks' <- sending sid cenv batch1 testCaseStep "pollingApiClient: polling for successful release" - pr <- liftIO $ polling sid cenv rks' ExpectPactResult + pr <- polling sid cenv rks' ExpectPactResult testCaseStep "localApiClient: retrieving account info for allocation02" - SubmitBatch batch2 <- liftIO - $ mkSingletonBatch iot allocation02KeyPair' tx5 n5 (pm "allocation02") Nothing + SubmitBatch batch2 <- mkSingletonBatch iot allocation02KeyPair' tx5 n5 (pm "allocation02") Nothing - liftIO $ localTestToRetry sid cenv (head (toList batch2)) (localAfterPollResponse pr) + localTestToRetry sid cenv (head (toList batch2)) (localAfterPollResponse pr) case r of Left e -> assertFailure $ "test failure: " <> show e @@ -677,7 +664,7 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do $ ObjectMap $ M.fromList [ (FieldKey "account", PLiteral $ LString "allocation00") - , (FieldKey "balance", PLiteral $ LDecimal 1_099_995.84) -- balance = (1k + 1mm) - gas + , (FieldKey "balance", PLiteral $ LDecimal 1_099_993.89) -- balance = (1k + 1mm) - gas , (FieldKey "guard", PGuard $ GKeySetRef (KeySetName "allocation00" Nothing)) ] @@ -702,7 +689,7 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do $ ObjectMap $ M.fromList [ (FieldKey "account", PLiteral $ LString "allocation02") - , (FieldKey "balance", PLiteral $ LDecimal 1_099_995.13) -- 1k + 1mm - gas + , (FieldKey "balance", PLiteral $ LDecimal 1_099_991) -- 1k + 1mm - gas , (FieldKey "guard", PGuard $ GKeySetRef (KeySetName "allocation02" Nothing)) ] @@ -731,7 +718,7 @@ mkSingletonBatch iot kps (PactTransaction c d) nonce pmk clist = do ks <- testKeyPairs kps clist pm <- pmk . toTxCreationTime <$> iot let dd = fromMaybe A.Null d - cmd <- liftIO $ Pact.mkExec c dd pm ks (Just "fastTimedCPM-peterson") nonce + cmd <- liftIO $ Pact.mkExec c dd pm ks (Just "fastfork-CPM-peterson") nonce return $ SubmitBatch (cmd NEL.:| []) withRequestKeys @@ -751,44 +738,12 @@ withRequestKeys iot ioNonce networkIO f = withResource mkKeys (\_ -> return ()) testSend :: IO (Time Micros) -> MVar Int -> ClientEnv -> IO RequestKeys testSend iot mNonce env = testBatch iot mNonce gp >>= sending cid env -awaitCutHeight - :: (String -> IO ()) - -> ClientEnv - -> CutHeight - -> IO CutHashes -awaitCutHeight step cenv i = do - result <- retrying testRetryPolicy checkRetry - $ const $ runClientM (cutGetClient v) cenv - case result of - Left e -> throwM e - Right x - | _cutHashesHeight x >= i -> return x - | otherwise -> throwM $ SlowChain - $ "retries exhausted: waiting for cut height " <> sshow i - <> " but only got " <> sshow (_cutHashesHeight x) - where - checkRetry s (Left e) = do - step $ "awaiting cut of height " <> show i - <> ". No result from node: " <> show e - <> " [" <> show (view rsIterNumberL s) <> "]" - return True - checkRetry s (Right c) - | _cutHashesHeight c >= i = return False - | otherwise = do - step - $ "awaiting cut of height " <> show i - <> ". Current cut height: " <> show (_cutHashesHeight c) - <> ". Current block heights: " <> show (_bhwhHeight <$> _cutHashes c) - <> " [" <> show (view rsIterNumberL s) <> "]" - return True - - testBatch'' :: Pact.ChainId -> IO (Time Micros) -> Integer -> MVar Int -> GasPrice -> IO SubmitBatch testBatch'' chain iot ttl mnonce gp' = modifyMVar mnonce $ \(!nn) -> do let nonce = "nonce" <> sshow nn t <- toTxCreationTime <$> iot kps <- testKeyPairs sender00 Nothing - c <- Pact.mkExec "(+ 1 2)" A.Null (pm t) kps (Just "fastTimedCPM-peterson") (Just nonce) + c <- Pact.mkExec "(+ 1 2)" A.Null (pm t) kps (Just "fastfork-CPM-peterson") (Just nonce) pure (succ nn, SubmitBatch (pure c)) where pm :: Pact.TxCreationTime -> Pact.PublicMeta diff --git a/test/Chainweb/Test/Pact/RewardsTest.hs b/test/Chainweb/Test/Pact/RewardsTest.hs index d8dfc720e5..2a14ff8b57 100644 --- a/test/Chainweb/Test/Pact/RewardsTest.hs +++ b/test/Chainweb/Test/Pact/RewardsTest.hs @@ -14,11 +14,11 @@ import Chainweb.Graph import Chainweb.Miner.Pact import Chainweb.Pact.PactService.ExecBlock import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Version - v :: ChainwebVersion -v = FastTimedCPM petersonChainGraph +v = fastForkingCpmTestVersion petersonChainGraph tests :: ScheduledTest tests = ScheduledTest "Chainweb.Test.Pact.RewardsTest" $ diff --git a/test/Chainweb/Test/Pact/SPV.hs b/test/Chainweb/Test/Pact/SPV.hs index c3d0a5570f..ffe56d4263 100644 --- a/test/Chainweb/Test/Pact/SPV.hs +++ b/test/Chainweb/Test/Pact/SPV.hs @@ -76,7 +76,6 @@ import Chainweb.BlockCreationTime import Chainweb.BlockHash import Chainweb.BlockHeader import Chainweb.BlockHeight -import Chainweb.ChainId import Chainweb.Cut import Chainweb.Graph import Chainweb.Miner.Pact @@ -88,6 +87,7 @@ import Chainweb.Test.Cut import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils hiding (check) @@ -116,10 +116,10 @@ tests = testGroup "Chainweb.Test.Pact.SPV" ] testVer :: ChainwebVersion -testVer = FastTimedCPM triangleChainGraph +testVer = noBridgeCpmTestVersion triangleChainGraph bridgeVer :: ChainwebVersion -bridgeVer = FastTimedCPM pairChainGraph +bridgeVer = fastForkingCpmTestVersion pairChainGraph logg :: LogMessage a => LogLevel -> a -> IO () logg l @@ -238,9 +238,9 @@ runCut' v bdb pact = do roundtrip - :: Int + :: Word32 -- ^ source chain id - -> Int + -> Word32 -- ^ target chain id -> BurnGenerator -- ^ burn tx generator @@ -252,9 +252,9 @@ roundtrip = roundtrip' testVer roundtrip' :: ChainwebVersion - -> Int + -> Word32 -- ^ source chain id - -> Int + -> Word32 -- ^ target chain id -> BurnGenerator -- ^ burn tx generator diff --git a/test/Chainweb/Test/Pact/TTL.hs b/test/Chainweb/Test/Pact/TTL.hs index b65aa6f19f..9fbc76242d 100644 --- a/test/Chainweb/Test/Pact/TTL.hs +++ b/test/Chainweb/Test/Pact/TTL.hs @@ -28,7 +28,6 @@ import Test.Tasty.HUnit import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeaderDB hiding (withBlockHeaderDb) import Chainweb.BlockHeaderDB.Internal (unsafeInsertBlockHeaderDb) import Chainweb.Miner.Pact @@ -42,6 +41,7 @@ import Chainweb.Payload.PayloadStore import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Pact.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils import Chainweb.Version @@ -53,7 +53,7 @@ import Chainweb.Storage.Table.RocksDB -- Settings testVer :: ChainwebVersion -testVer = FastTimedCPM peterson +testVer = fastForkingCpmTestVersion peterson genblock :: BlockHeader genblock = genesisBlockHeader testVer (someChainId testVer) diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index f386bc5cd0..b9273dfe6f 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -58,9 +58,12 @@ import Chainweb.Pact.Templates import Chainweb.Pact.TransactionExec import Chainweb.Pact.Types import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils import Chainweb.Version as V +import Chainweb.Version.Development +import Chainweb.Version.Mainnet import Chainweb.Test.Pact.Utils @@ -183,11 +186,11 @@ fixedInjTest = case exec of buildExecWithData :: Assertion -buildExecWithData = void $ buildExecParsedCode Nothing +buildExecWithData = void $ buildExecParsedCode maxBound (Just $ object [ "data" .= (1 :: Int) ]) "(+ 1 1)" buildExecWithoutData :: Assertion -buildExecWithoutData = void $ buildExecParsedCode Nothing Nothing "(+ 1 1)" +buildExecWithoutData = void $ buildExecParsedCode maxBound Nothing "(+ 1 1)" badMinerId :: MinerId badMinerId = MinerId "alpha\" (read-keyset \"miner-keyset\") 9999999.99)(coin.coinbase \"alpha" @@ -206,7 +209,7 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do step "pre-fork code injection succeeds, no enforced precompile" - cmd <- buildExecParsedCode Nothing Nothing "(coin.get-balance \"tester01\")" + cmd <- buildExecParsedCode maxBound Nothing "(coin.get-balance \"tester01\")" doCoinbaseExploit pdb mc preForkHeight cmd False $ \case Left _ -> assertFailure "local call to get-balance failed" @@ -217,7 +220,7 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do step "post-fork code injection fails, no enforced precompile" - cmd' <- buildExecParsedCode Nothing Nothing + cmd' <- buildExecParsedCode maxBound Nothing "(coin.get-balance \"tester01\\\" (read-keyset \\\"miner-keyset\\\") 1000.0)(coin.coinbase \\\"tester01\")" doCoinbaseExploit pdb mc postForkHeight cmd' False $ \case @@ -273,10 +276,9 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do -- of mining a full chain we fake the height. -- mkTestParentHeader :: BlockHeight -> ParentHeader - mkTestParentHeader h = ParentHeader $ (someBlockHeader (FastTimedCPM singleton) 10) + mkTestParentHeader h = ParentHeader $ (someBlockHeader (slowForkingCpmTestVersion singleton) 10) { _blockHeight = h } - testCoinbaseEnforceFailure :: Assertion testCoinbaseEnforceFailure = do (pdb,mc) <- loadCC coinReplV4 @@ -317,17 +319,17 @@ testCoinbaseUpgradeDevnet cid upgradeHeight = testTwentyChainDevnetUpgrades :: TestTree testTwentyChainDevnetUpgrades = testCaseSteps "Test 20-chain Devnet upgrades" $ \step -> do step "Check that 20-chain upgrades fire at block height 150" - testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) V.to20ChainsDevelopment test0 + testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) 12 test0 step "Check that 20-chain upgrades do not fire at block heights < 150 and > 150" - testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (V.to20ChainsDevelopment - 1) test1 - testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (V.to20ChainsDevelopment + 1) test1 + testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (12 - 1) test1 + testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (12 + 1) test1 step "Check that 20-chain upgrades do not fire at on other chains" - testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 1) V.to20ChainsDevelopment test1 + testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 1) 12 test1 step "Check that 20-chain upgrades succeed even if e7f7 balance is insufficient" - testUpgradeScript "test/pact/twenty-chain-insufficient-bal.repl" (unsafeChainId 0) V.to20ChainsDevelopment test1 + testUpgradeScript "test/pact/twenty-chain-insufficient-bal.repl" (unsafeChainId 0) 12 test1 where test0 (T2 cr _) = case _crLogs cr of Just logs -> matchLogs (logResults logs) @@ -364,7 +366,7 @@ testUpgradeScript script cid bh test = do where p = parent bh cid -matchLogs :: [(Text, Text, Maybe Value)] -> [(Text, Text, Maybe Value)] -> IO () +matchLogs :: HasCallStack => [(Text, Text, Maybe Value)] -> [(Text, Text, Maybe Value)] -> IO () matchLogs expectedResults actualResults | length actualResults /= length expectedResults = void $ assertFailure $ intercalate "\n" $ @@ -381,7 +383,7 @@ matchLogs expectedResults actualResults (assertEqual "balance matches" `on` view _3) actual expected parent :: BlockHeight -> V.ChainId -> ParentHeader -parent bh cid = ParentHeader $ (someBlockHeader v bh) +parent bh cid = ParentHeader (someBlockHeader v bh) { _blockChainwebVersion = v , _blockChainId = cid , _blockHeight = pred bh diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index 2f48d75dee..15291ef924 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -140,6 +140,7 @@ import Data.String import qualified Data.Vector as V import GHC.Generics +import GHC.Stack import System.Directory import System.IO.Temp (createTempDirectory) @@ -173,10 +174,10 @@ import Pact.Types.Util (parseB16TextOnly) import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeaderDB hiding (withBlockHeaderDb) import Chainweb.BlockHeight import Chainweb.ChainId +import Chainweb.Graph import Chainweb.Logger import Chainweb.Miner.Pact import Chainweb.Pact.Backend.RelationalCheckpointer @@ -195,10 +196,11 @@ import Chainweb.Test.Cut import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Utils import Chainweb.Test.Utils.BlockHeader +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils -import Chainweb.Version (ChainwebVersion(..), chainIds) +import Chainweb.Version import qualified Chainweb.Version as Version import Chainweb.Version.Utils (someChainId) import Chainweb.WebBlockHeaderDB @@ -213,6 +215,7 @@ import Chainweb.Storage.Table.RocksDB type SimpleKeyPair = (Text,Text) -- | Legacy; better to use 'CmdSigner'/'CmdBuilder'. +-- if caps are empty, gas cap is implicit. otherwise it must be included testKeyPairs :: SimpleKeyPair -> Maybe [SigCapability] -> IO [SomeKeyPairCaps] testKeyPairs skp capsm = do kp <- toApiKp $ mkSigner' skp (fromMaybe [] capsm) @@ -609,10 +612,10 @@ testPactCtxSQLite v cid bhdb pdb sqlenv conf gasmodel = do !ctx <- TestPactCtx <$!> newMVar (PactServiceState Nothing mempty ph noSPVSupport) <*> pure (pactServiceEnv cpe rs) - evalPactServiceM_ ctx (initialPayloadState dummyLogger mempty v cid) + evalPactServiceM_ ctx (initialPayloadState (genericLogger Info T.putStrLn) mempty v cid) return (ctx, PactDbEnv' dbSt) where - initialBlockState = initBlockState defaultModuleCacheLimit $ Version.genesisHeight v cid + initialBlockState = initBlockState defaultModuleCacheLimit $ genesisHeight v cid loggers = pactTestLogger False -- toggle verbose pact test logging cpLogger = newLogger loggers $ LogName ("Checkpointer" ++ show cid) pactServiceEnv cpe rs = PactServiceEnv @@ -811,7 +814,8 @@ withTemporaryDir = withResource removeDirectoryRecursive withTestBlockDbTest - :: ChainwebVersion + :: HasCallStack + => ChainwebVersion -> RocksDb -> (IO TestBlockDb -> TestTree) -> TestTree @@ -819,7 +823,8 @@ withTestBlockDbTest v rdb = withResource (mkTestBlockDb v rdb) mempty -- | Single-chain Pact via service queue. withPactTestBlockDb - :: ChainwebVersion + :: HasCallStack + => ChainwebVersion -> ChainId -> LogLevel -> RocksDb @@ -852,7 +857,7 @@ dummyLogger :: GenericLogger dummyLogger = genericLogger Quiet T.putStrLn someTestVersion :: ChainwebVersion -someTestVersion = FastTimedCPM peterson +someTestVersion = fastForkingCpmTestVersion petersonChainGraph someTestVersionHeader :: BlockHeader someTestVersionHeader = someBlockHeader someTestVersion 10 diff --git a/test/Chainweb/Test/RestAPI.hs b/test/Chainweb/Test/RestAPI.hs index 25d79f706c..91aa5730cf 100644 --- a/test/Chainweb/Test/RestAPI.hs +++ b/test/Chainweb/Test/RestAPI.hs @@ -41,7 +41,6 @@ import Text.Read (readEither) -- internal modules import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) import Chainweb.BlockHeaderDB import Chainweb.BlockHeaderDB.Internal (unsafeInsertBlockHeaderDb) import Chainweb.ChainId @@ -51,6 +50,7 @@ import Chainweb.RestAPI import Chainweb.Test.RestAPI.Client_ import Chainweb.Test.Utils import Chainweb.Test.Utils.BlockHeader +import Chainweb.Test.TestVersions (barebonesTestVersion) import Chainweb.TreeDB import Chainweb.Utils import Chainweb.Utils.Paging @@ -103,11 +103,12 @@ tests rdb = testGroup "REST API tests" tests_ :: RocksDb -> Bool -> [TestTree] tests_ rdb tls = - [ simpleSessionTests rdb tls version - , pagingTests rdb tls version + [ simpleSessionTests rdb tls + , pagingTests rdb tls ] - where - version = Test singletonChainGraph + +version :: ChainwebVersion +version = barebonesTestVersion singletonChainGraph -- -------------------------------------------------------------------------- -- -- Test all endpoints on each chain @@ -119,8 +120,8 @@ type TestClientEnv_ = TestClientEnv MockTx RocksDbTable noMempool :: [(ChainId, MempoolBackend MockTx)] noMempool = [] -simpleSessionTests :: RocksDb -> Bool -> ChainwebVersion -> TestTree -simpleSessionTests rdb tls version = +simpleSessionTests :: RocksDb -> Bool -> TestTree +simpleSessionTests rdb tls = withBlockHeaderDbsResource rdb version $ \dbs -> withBlockHeaderDbsServer tls version dbs (return noMempool) $ \env -> testGroup "client session tests" @@ -140,7 +141,7 @@ httpHeaderTests envIO cid = ] where go name run = testCase name $ do - BlockHeaderDbsTestClientEnv env _ version <- liftIO envIO + BlockHeaderDbsTestClientEnv env _ _ <- liftIO envIO res <- flip runClientM_ env $ modifyResponse checkHeader $ run version (genesisBlockHeader version cid) assertBool ("test failed: " <> sshow res) (isRight res) @@ -162,12 +163,12 @@ httpHeaderTests envIO cid = simpleClientSession :: IO TestClientEnv_ -> ChainId -> TestTree simpleClientSession envIO cid = testCaseSteps ("simple session for chain " <> sshow cid) $ \step -> do - BlockHeaderDbsTestClientEnv env dbs version <- envIO - res <- runClientM (session version dbs step) env + BlockHeaderDbsTestClientEnv env dbs _ <- envIO + res <- runClientM (session dbs step) env assertBool ("test failed: " <> sshow res) (isRight res) where - session version dbs step = do + session dbs step = do let gbh0 = genesisBlockHeader version cid @@ -326,14 +327,14 @@ simpleClientSession envIO cid = -- -------------------------------------------------------------------------- -- -- Paging Tests -pagingTests :: RocksDb -> Bool -> ChainwebVersion -> TestTree -pagingTests rdb tls version = +pagingTests :: RocksDb -> Bool -> TestTree +pagingTests rdb tls = withBlockHeaderDbsServer tls version (starBlockHeaderDbs 6 $ testBlockHeaderDbs rdb version) (return noMempool) $ \env -> testGroup "paging tests" - [ testPageLimitHeadersClient version env - , testPageLimitHashesClient version env + [ testPageLimitHeadersClient env + , testPageLimitHashesClient env ] pagingTest @@ -417,12 +418,12 @@ pagingTest name getDbItems getKey fin request envIO = testGroup name | n >= len ents = Exclusive . getKey <$> (Just $ last ents) | otherwise = Inclusive . getKey <$> listToMaybe (drop (int n) ents) -testPageLimitHeadersClient :: ChainwebVersion -> IO TestClientEnv_ -> TestTree -testPageLimitHeadersClient version = pagingTest "headersClient" headers key False request +testPageLimitHeadersClient :: IO TestClientEnv_ -> TestTree +testPageLimitHeadersClient = pagingTest "headersClient" headers key False request where request cid l n = headersClient version cid l n Nothing Nothing -testPageLimitHashesClient :: ChainwebVersion -> IO TestClientEnv_ -> TestTree -testPageLimitHashesClient version = pagingTest "hashesClient" hashes id False request +testPageLimitHashesClient :: IO TestClientEnv_ -> TestTree +testPageLimitHashesClient = pagingTest "hashesClient" hashes id False request where request cid l n = hashesClient version cid l n Nothing Nothing diff --git a/test/Chainweb/Test/RestAPI/Client_.hs b/test/Chainweb/Test/RestAPI/Client_.hs index dd3ec9bf1a..ddf04756f4 100644 --- a/test/Chainweb/Test/RestAPI/Client_.hs +++ b/test/Chainweb/Test/RestAPI/Client_.hs @@ -68,7 +68,7 @@ payloadGetClient' -> BlockPayloadHash -> ClientM_ PayloadData payloadGetClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(PayloadGetApi v c) @@ -78,7 +78,7 @@ outputsGetClient' -> BlockPayloadHash -> ClientM_ PayloadWithOutputs outputsGetClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(OutputsGetApi v c) @@ -90,7 +90,7 @@ cutGetClient' -> Maybe MaxRank -> ClientM_ CutHashes cutGetClient' v = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) return $ client_ @(CutGetApi v) cutPutClient' @@ -98,7 +98,7 @@ cutPutClient' -> CutHashes -> ClientM_ NoContent cutPutClient' v = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) return $ client_ @(CutPutApi v) -- -------------------------------------------------------------------------- -- @@ -106,7 +106,7 @@ cutPutClient' v = runIdentity $ do headerClient' :: ChainwebVersion -> ChainId -> BlockHash -> ClientM_ BlockHeader headerClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(HeaderApi v c) @@ -119,7 +119,7 @@ headersClient' -> Maybe MaxRank -> ClientM_ (Page (NextItem BlockHash) BlockHeader) headersClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(HeadersApi v c) @@ -132,7 +132,7 @@ hashesClient' -> Maybe MaxRank -> ClientM_ (Page (NextItem BlockHash) BlockHash) hashesClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(HashesApi v c) @@ -146,7 +146,7 @@ branchHashesClient' -> BranchBounds BlockHeaderDb -> ClientM_ (Page (NextItem BlockHash) BlockHash) branchHashesClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(BranchHashesApi v c) @@ -160,6 +160,6 @@ branchHeadersClient' -> BranchBounds BlockHeaderDb -> ClientM_ (Page (NextItem BlockHash) BlockHeader) branchHeadersClient' v c = runIdentity $ do - (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing v + (SomeSing (SChainwebVersion :: Sing v)) <- return $ toSing (_versionName v) (SomeSing (SChainId :: Sing c)) <- return $ toSing c return $ client_ @(BranchHeadersApi v c) diff --git a/test/Chainweb/Test/RestAPI/Utils.hs b/test/Chainweb/Test/RestAPI/Utils.hs index 4caa840644..8159c60610 100644 --- a/test/Chainweb/Test/RestAPI/Utils.hs +++ b/test/Chainweb/Test/RestAPI/Utils.hs @@ -4,11 +4,8 @@ {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} module Chainweb.Test.RestAPI.Utils -( -- * Retry Policies - testRetryPolicy - -- * Debugging -, debug +( debug -- * Utils , repeatUntil @@ -67,6 +64,8 @@ import Chainweb.Pact.Service.Types import Chainweb.Rosetta.RestAPI.Client import Chainweb.Utils import Chainweb.Version +import Chainweb.Test.TestVersions +import Chainweb.Test.Utils -- internal pact modules @@ -85,18 +84,7 @@ debug = const $ return () #endif v :: ChainwebVersion -v = FastTimedCPM petersonChainGraph - --- | Backoff up to a constant 250ms, limiting to ~40s --- (actually saw a test have to wait > 22s) -testRetryPolicy :: RetryPolicy -testRetryPolicy = stepped <> limitRetries 150 - where - stepped = retryPolicy $ \rs -> case rsIterNumber rs of - 0 -> Just 20_000 - 1 -> Just 50_000 - 2 -> Just 100_000 - _ -> Just 250_000 +v = fastForkingCpmTestVersion petersonChainGraph -- ------------------------------------------------------------------ -- -- Pact api client utils w/ retry @@ -107,7 +95,6 @@ data PactTestFailure | SendFailure String | LocalFailure String | SpvFailure String - | SlowChain String deriving Show instance Exception PactTestFailure diff --git a/test/Chainweb/Test/Rosetta.hs b/test/Chainweb/Test/Rosetta.hs index 06b2112447..eb69877247 100644 --- a/test/Chainweb/Test/Rosetta.hs +++ b/test/Chainweb/Test/Rosetta.hs @@ -43,6 +43,8 @@ import Chainweb.Rosetta.Internal import Chainweb.Rosetta.RestAPI import Chainweb.Rosetta.Utils import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Testnet import qualified Pact.Types.KeySet as P --- diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 9f7b2a17ef..5042153f6a 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -11,6 +11,7 @@ module Chainweb.Test.Rosetta.RestAPI ) where +import Debug.Trace import Control.Concurrent.Async import Control.Concurrent.MVar import Control.Lens @@ -46,13 +47,17 @@ import qualified Pact.Types.PactValue as P -- internal chainweb modules +import Chainweb.BlockHeight import Chainweb.Graph import Chainweb.Pact.Utils (aeson) -import Chainweb.Pact.Transactions.UpgradeTransactions +import qualified Chainweb.Pact.Transactions.OtherTransactions as Other +import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 +import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD import Chainweb.Rosetta.Utils import Chainweb.Test.Pact.Utils import Chainweb.Test.RestAPI.Utils import Chainweb.Test.Utils +import Chainweb.Test.TestVersions import Chainweb.Time (Time(..), Micros(..)) import Chainweb.Utils import Chainweb.Version @@ -68,7 +73,7 @@ import System.IO.Unsafe (unsafePerformIO) -- Global Settings v :: ChainwebVersion -v = FastTimedCPM petersonChainGraph +v = fastForkingCpmTestVersion petersonChainGraph nodes :: Natural nodes = 1 @@ -96,6 +101,9 @@ gasCost units = realToFrac units * defGasPrice defMiningReward :: Decimal defMiningReward = 2.304523 +transferGasCost :: Decimal +transferGasCost = gasCost 700 + type RosettaTest = IO (Time Micros) -> IO ClientEnv -> ScheduledTest -- -------------------------------------------------------------------------- -- @@ -122,8 +130,7 @@ tests rdb = testGroupSch "Chainweb.Test.Rosetta.RestAPI" go -- tgroup tio envIo = fmap (\test -> test tio envIo) - [ blockTests "Block Test with transfer and potential coin v2 remediation" - , blockTests "Block Test with transfer and potential chain 20 remediation" + [ waitForRemediations , blockTransactionTests , blockCoinV2RemediationTests , block20ChainRemediationTests @@ -136,10 +143,13 @@ tests rdb = testGroupSch "Chainweb.Test.Rosetta.RestAPI" go , blockKAccountAfterPact420 , constructionTransferTests , blockCoinV3RemediationTests - -- Note (linda): The order of the above tests matters. - -- So when adding new tests, add to the bottom of the list if possible. ] +waitForRemediations :: RosettaTest +waitForRemediations _tio envIo = + testCaseSchSteps "Wait for cut height post-remediations" $ \step -> + awaitBlockHeight v step envIo (latestBehaviorAt v) + -- | Rosetta account balance endpoint tests -- accountBalanceTests :: RosettaTest @@ -148,17 +158,19 @@ accountBalanceTests tio envIo = step "check initial balance" cenv <- envIo resp0 <- accountBalance cenv req - checkBalance resp0 99999995.7812 + let startBal = 99999997.8600 + checkBalance resp0 startBal step "send 1.0 tokens to sender00 from sender01" void $! transferOneAsync_ cid tio cenv (void . return) step "check post-transfer and gas fees balance" resp1 <- accountBalance cenv req - checkBalance resp1 99999994.7265 + checkBalance resp1 (startBal - transferGasCost - 1) where req = AccountBalanceReq nid (AccountId "sender00" Nothing Nothing) Nothing + checkBalance :: HasCallStack => AccountBalanceResp -> Decimal -> IO () checkBalance resp bal1 = do let b0 = head $ _accountBalanceResp_balances resp b1 = kdaToRosettaAmount bal1 @@ -226,8 +238,6 @@ blockTransactionTests tio envIo = validateOp 4 "GasPayment" noMinerks Successful transferGasCost reward where - transferGasCost = gasCost 547 - mkTxReq rkmv prs = do rk <- NEL.head . _rkRequestKeys <$> takeMVar rkmv meta <- extractMetadata rk prs @@ -262,7 +272,6 @@ blockTests testname tio envIo = testCaseSchSteps testname $ \step -> do validateTransferResp bh resp1 where req h = BlockReq nid $ PartialBlockId (Just h) Nothing - transferGasCost = gasCost 547 validateTransferResp bh resp = do _blockResp_otherTransactions resp @?= Nothing @@ -274,23 +283,6 @@ blockTests testname tio envIo = testCaseSchSteps testname $ \step -> do _blockId_index (_block_parentBlockId b) @?= (bh - 1) case _block_transactions b of - [x,r1,r2,y] -> do - -- coin v2 remediation block. - -- No coin table remediation for this version. - let ops = _transaction_operations x <> _transaction_operations r1 <> - _transaction_operations r2 <> _transaction_operations y - case ops of - [a,b',c,d,e,f] -> validateTxs Nothing a b' c d e f - _ -> assertFailure "should have 6 ops: coinbase + 5 for transfer tx" - - [x,r1,y] -> do - -- 20 chain remediation block - let ops = _transaction_operations x <> _transaction_operations r1 <> - _transaction_operations y - case ops of - [a,rop1, b',c,d,e,f] -> validateTxs (Just rop1) a b' c d e f - _ -> assertFailure "should have 7 ops: coinbase + 20 chain rem + 5 for transfer tx" - [x,y] -> do -- not a remediation block let ops = _transaction_operations x <> _transaction_operations y @@ -302,7 +294,7 @@ blockTests testname tio envIo = testCaseSchSteps testname $ \step -> do validateBlock $ _blockResp_block resp - validateTxs remeds cbase fundtx cred deb redeem reward = do + validateTxs remeds cbase fundtx cred deb gasRedeem gasReward = do -- coinbase is considered a separate tx list validateOp 0 "CoinbaseReward" noMinerks Successful defMiningReward cbase @@ -316,8 +308,8 @@ blockTests testname tio envIo = testCaseSchSteps testname $ \step -> do validateOp 0 "FundTx" sender00ks Successful (negate defFundGas) fundtx validateOp 1 "TransferOrCreateAcct" sender01ks Successful 1.0 cred validateOp 2 "TransferOrCreateAcct" sender00ks Successful (negate 1.0) deb - validateOp 3 "GasPayment" sender00ks Successful (defFundGas - transferGasCost) redeem - validateOp 4 "GasPayment" noMinerks Successful transferGasCost reward + validateOp 3 "GasPayment" sender00ks Successful (defFundGas - transferGasCost) gasRedeem + validateOp 4 "GasPayment" noMinerks Successful transferGasCost gasReward blockCoinV2RemediationTests :: RosettaTest blockCoinV2RemediationTests _ envIo = @@ -337,7 +329,7 @@ blockCoinV2RemediationTests _ envIo = case _block_transactions b of x:y:z:_ -> do step "check remediation transactions' request keys" - [ycmd, zcmd] <- upgradeTransactions v cid + [ycmd, zcmd] <- return Other.transactions _transaction_transactionId y @?= pactHashToTransactionId (_cmdHash ycmd) _transaction_transactionId z @?= pactHashToTransactionId (_cmdHash zcmd) @@ -353,7 +345,7 @@ blockCoinV2RemediationTests _ envIo = _ -> assertFailure $ "coin v2 remediation block should have at least 3 transactions:" ++ " coinbase + 2 remediations" where - bhCoinV2Rem = 1 + bhCoinV2Rem = v ^?! versionForks . at CoinV2 . _Just . onChain cid . to getBlockHeight req h = BlockReq nid $ PartialBlockId (Just h) Nothing block20ChainRemediationTests :: RosettaTest @@ -374,7 +366,7 @@ block20ChainRemediationTests _ envIo = case _block_transactions b of x:y:_ -> do step "check remediation transactions' request keys" - [ycmd] <- twentyChainUpgradeTransactions v cidChain3 + [ycmd] <- return MNKAD.transactions _transaction_transactionId y @?= pactHashToTransactionId (_cmdHash ycmd) step "check remediation transactions' operations" @@ -388,13 +380,11 @@ block20ChainRemediationTests _ envIo = _ -> assertFailure $ "20 chain remediation block should have at least 2 transactions:" ++ " coinbase + 1 remediations" where - {-- NOTE: FastTimedCPM 20 chain remediations occurs on chain 3 and block height 2. - See Chainweb.Pact.Transactions.UpgradeTransactions and Chainweb.Version. --} - cidChain3 = unsafeChainId 3 + -- edtodo: extract this from the version(?) bhChain20Rem = 2 nidChain3 = NetworkId { _networkId_blockchain = "kadena" - , _networkId_network = "fastTimedCPM-peterson" + , _networkId_network = "fastfork-CPM-peterson" , _networkId_subNetworkId = Just (SubNetworkId "3" Nothing) } req h = BlockReq nidChain3 $ PartialBlockId (Just h) Nothing @@ -417,7 +407,7 @@ blockCoinV3RemediationTests _ envIo = case _block_transactions b of x:y:_ -> do step "check remediation transactions' request keys" - [ycmd] <- coinV3Transactions + [ycmd] <- return CoinV3.transactions _transaction_transactionId y @?= pactHashToTransactionId (_cmdHash ycmd) step "check remediation transactions' operations" @@ -431,7 +421,7 @@ blockCoinV3RemediationTests _ envIo = _ -> assertFailure $ "coin v3 remediation block should have at least 3 transactions:" ++ " coinbase + 2 remediations" where - bhCoinV3Rem = 20 + bhCoinV3Rem = v ^?! versionForks . at Pact4Coin3 . _Just . onChain cid . to getBlockHeight req h = BlockReq nid $ PartialBlockId (Just h) Nothing -- | Rosetta construction endpoints tests (i.e. tx formatting and submission) @@ -521,7 +511,8 @@ constructionTransferTests _ envIo = getKeys _ = Nothing submitToConstructionAPI - :: [Operation] + :: HasCallStack + => [Operation] -> ChainId -> Text -> (Text -> Maybe SimpleKeyPair) @@ -662,7 +653,7 @@ networkListTests _ envIo = for_ (_networkListResp_networkIds resp) $ \n -> do _networkId_blockchain n @=? "kadena" - _networkId_network n @=? "fastTimedCPM-peterson" + _networkId_network n @=? "fastfork-CPM-peterson" assertBool "chain id of subnetwork is valid" $ maybe False (\a -> _subNetworkId_network a `elem` cids) $ _networkId_subNetworkId n @@ -731,7 +722,7 @@ kda = Currency "KDA" 12 Nothing nid :: NetworkId nid = NetworkId { _networkId_blockchain = "kadena" - , _networkId_network = "fastTimedCPM-peterson" + , _networkId_network = "fastfork-CPM-peterson" , _networkId_subNetworkId = Just (SubNetworkId (chainIdToText cid) Nothing) } @@ -745,7 +736,7 @@ rosettaVersion = RosettaNodeVersion , _version_middlewareVersion = Nothing , _version_metadata = Just $ HM.fromList [ "node-api-version" A..= ("0.0" :: Text) - , "chainweb-version" A..= ("fastTimedCPM-peterson" :: Text) + , "chainweb-version" A..= ("fastfork-CPM-peterson" :: Text) , "rosetta-chainweb-version" A..= ("2.0.0" :: Text) ] } @@ -770,7 +761,8 @@ operationTypes = -- | Validate all useful data for a tx operation -- validateOp - :: Word64 + :: HasCallStack + => Word64 -- ^ op idx -> Text -- ^ operation type diff --git a/test/Chainweb/Test/Roundtrips.hs b/test/Chainweb/Test/Roundtrips.hs index 7b9ab936b2..25e18854e1 100644 --- a/test/Chainweb/Test/Roundtrips.hs +++ b/test/Chainweb/Test/Roundtrips.hs @@ -1,6 +1,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-orphans #-} -- | -- Module: Chainweb.Test.Roundtrips @@ -101,8 +102,8 @@ tests = testGroup "roundtrip tests" encodeDecodeTests :: TestTree encodeDecodeTests = testGroup "Encode-Decode roundtrips" - [ testProperty "ChainwebVersion" - $ prop_encodeDecode decodeChainwebVersion encodeChainwebVersion + [ testProperty "ChainwebVersionCode" + $ prop_encodeDecode decodeChainwebVersionCode encodeChainwebVersionCode , testProperty "ChainId" $ prop_encodeDecode decodeChainId encodeChainId , testProperty "MerkleLogHash" @@ -215,6 +216,9 @@ encodeDecodeTests = testGroup "Encode-Decode roundtrips" -- -------------------------------------------------------------------------- -- -- JSON +instance Arbitrary MockTx where + arbitrary = MockTx <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary + jsonTestCases :: (forall a . Arbitrary a => Show a => ToJSON a => FromJSON a => Eq a => a -> Property) -> [TestTree] @@ -224,7 +228,7 @@ jsonTestCases f = , testProperty "Seconds" $ f @Seconds , testProperty "Micros" $ f @Micros , testProperty "ChainId" $ f @ChainId - , testProperty "ChainwebVersion" $ f @ChainwebVersion + , testProperty "ChainwebVersionName" $ f @ChainwebVersionName , testProperty "Nonce" $ f @Nonce , testProperty "HashDifficulty" $ f @HashDifficulty , testProperty "HashTarget" $ f @HashTarget @@ -429,8 +433,7 @@ base64RoundtripTests = testGroup "Base64 encoding roundtrips" hasTextRepresentationTests :: TestTree hasTextRepresentationTests = testGroup "HasTextRepresentation roundtrips" - [ testProperty "ChainwebVersion" $ prop_iso' @_ @ChainwebVersion fromText toText - , testProperty "ChainwebVersion" $ prop_iso' @_ @ChainwebVersion eitherFromText toText + [ testProperty "ChainwebVersionName" $ prop_iso' @_ @ChainwebVersionName eitherFromText toText , testProperty "ChainId" $ prop_iso' @_ @ChainId fromText toText , testProperty "BlockHash" $ prop_iso' @_ @BlockHash fromText toText , testProperty "Seconds" $ prop_iso' @_ @Seconds fromText toText diff --git a/test/Chainweb/Test/SPV.hs b/test/Chainweb/Test/SPV.hs index f9a97689c9..e81f187461 100644 --- a/test/Chainweb/Test/SPV.hs +++ b/test/Chainweb/Test/SPV.hs @@ -75,6 +75,7 @@ import Chainweb.SPV.VerifyProof import Chainweb.Test.CutDB hiding (tests) import Chainweb.Test.Orphans.Internal import Chainweb.Test.Utils +import Chainweb.Test.TestVersions(barebonesTestVersion) import Chainweb.TreeDB import Chainweb.Utils hiding ((==>)) import Chainweb.Version @@ -82,6 +83,9 @@ import Chainweb.Version import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB +version :: ChainwebVersion +version = barebonesTestVersion petersonChainGraph + -- -------------------------------------------------------------------------- -- -- Test Tree @@ -89,15 +93,14 @@ import Chainweb.Storage.Table.RocksDB -- quickCheck instead of HUnit or should be derandomized. -- tests :: RocksDb -> TestTree -tests rdb = testGroup "SPV tests" - [ testCaseStepsN "SPV transaction proof" 10 (spvTransactionRoundtripTest rdb version) - , testCaseStepsN "SPV transaction output proof" 10 (spvTransactionOutputRoundtripTest rdb version) - , apiTests rdb version - , testCaseSteps "SPV transaction proof test" (spvTest rdb version) - , properties - ] - where - version = Test petersonChainGraph +tests rdb = + testGroup "SPV tests" + [ testCaseStepsN "SPV transaction proof" 10 (spvTransactionRoundtripTest rdb version) + , testCaseStepsN "SPV transaction output proof" 10 (spvTransactionOutputRoundtripTest rdb version) + , apiTests rdb version + , testCaseSteps "SPV transaction proof test" (spvTest rdb version) + , properties + ] -- -------------------------------------------------------------------------- -- -- Utils diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs new file mode 100644 index 0000000000..bc68e4e5f1 --- /dev/null +++ b/test/Chainweb/Test/TestVersions.hs @@ -0,0 +1,269 @@ +{-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Chainweb.Test.TestVersions + ( legalizeTestVersion + , barebonesTestVersion + , fastForkingCpmTestVersion + , noBridgeCpmTestVersion + , slowForkingCpmTestVersion + , timedConsensusVersion + ) where + +import Control.Lens hiding (elements) +import Data.Bits +import Data.Foldable +import Data.HashMap.Strict (HashMap) +import qualified Data.HashMap.Strict as HM +import qualified Data.HashSet as HS +import Data.Word +import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload as TN0 +import qualified Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload as TNN + +import System.IO.Unsafe + +-- internal modules + +import Chainweb.BlockCreationTime +import Chainweb.BlockHeight +import Chainweb.Difficulty +import Chainweb.Graph +import Chainweb.HostAddress +import Chainweb.Time +import Chainweb.Utils +import Chainweb.Utils.Rule +import Chainweb.Version +import Chainweb.Version.Registry +import P2P.Peer + +import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 +import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 +import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 +import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD +import qualified Chainweb.Pact.Transactions.OtherTransactions as Other + +testBootstrapPeerInfos :: PeerInfo +testBootstrapPeerInfos = + PeerInfo +#if WITH_ED25519 + { _peerId = Just $ unsafeFromText "BMe2hSdSEGCzLwvoYXPuB1BqYEH5wiV5AvacutSGWmg" +#else + { _peerId = Just $ unsafeFromText "9LkpIG95q5cs0YJg0d-xdR2YLeW_puv1PjS2kEfmEuQ" +#endif + -- this is the fingerprint of the certificate and key that is stored + -- in ./scripts/test-bootstrap-node.config". For programatic use of + -- the same certificate is also available at + -- "Chainweb.Test.P2P.Peer.BootstrapConfig". It is intended for + -- testing purposes only. + + , _peerAddr = HostAddress + { _hostAddressHost = localhost + , _hostAddressPort = 1789 + } + } + +data GraphPos = P1 | P2 deriving (Bounded, Enum) + +graphToCodeN :: GraphPos -> KnownGraph -> Word32 +graphToCodeN p g = shiftL (graphToCode g) (4 * (4 + fromEnum p)) + where + graphToCode :: KnownGraph -> Word32 + graphToCode Singleton = 0x00000001 + graphToCode Pair = 0x00000002 + graphToCode Triangle = 0x00000003 + graphToCode Peterson = 0x00000004 + graphToCode Twenty = 0x00000005 + graphToCode HoffmanSingleton = 0x00000006 + +legalizeTestVersion :: (ChainwebVersion -> ChainwebVersion) -> ChainwebVersion +legalizeTestVersion f = unsafePerformIO $ do + let v = f v + registerVersion v + return v + +-- edtodo: test registry list for codes +testRegistry :: [ChainwebVersion] +testRegistry = concat + [ [ fastForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] + , [ slowForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] + , [ barebonesTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] + , [ noBridgeCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] + , [ timedConsensusVersion (knownChainGraph g1) (knownChainGraph g2) | g1 :: KnownGraph <- [minBound..maxBound], g2 :: KnownGraph <- [minBound..maxBound] ] + ] + +testVersionTemplate :: ChainwebVersion -> ChainwebVersion +testVersionTemplate v = v + & versionHeaderBaseSizeBytes .~ 318 - 110 + & versionWindow .~ Nothing + & versionFakeFirstEpochStart .~ False -- DA is already disabled with window = Nothing + & versionMaxBlockGasLimit .~ End (Just 2_000_000) + & versionBootstraps .~ [testBootstrapPeerInfos] + +fastForks :: HashMap Fork (ChainMap BlockHeight) +fastForks = HM.fromList + [ (Pact420, AllChains (BlockHeight 0)) + , (SlowEpoch, AllChains (BlockHeight 0)) + , (OldTargetGuard, AllChains (BlockHeight 0)) + , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) + , (OldDAGuard, AllChains (BlockHeight 0)) + , (Vuln797Fix, AllChains (BlockHeight 0)) + , (PactBackCompat_v16, AllChains (BlockHeight 0)) + , (SPVBridge, AllChains (BlockHeight 0)) + , (EnforceKeysetFormats, AllChains (BlockHeight 0)) + , (CheckTxHash, AllChains (BlockHeight 0)) + , (Pact44NewTrans, AllChains (BlockHeight 0)) + , (Chainweb213Pact, AllChains (BlockHeight 0)) + , (PactEvents, AllChains (BlockHeight 0)) + , (CoinV2, AllChains (BlockHeight 1)) + , (SkipTxTimingValidation, AllChains (BlockHeight 2)) + , (ModuleNameFix, AllChains (BlockHeight 2)) + , (ModuleNameFix2, AllChains (BlockHeight 2)) + , (Pact4Coin3, AllChains (BlockHeight 4)) + , (Chainweb214Pact, AllChains (BlockHeight 5)) + , (Chainweb215Pact, AllChains (BlockHeight 10)) + , (Chainweb216Pact, AllChains (BlockHeight 16)) + , (Chainweb217Pact, AllChains (BlockHeight 20)) + ] + +barebonesTestVersion :: ChainGraph -> ChainwebVersion +barebonesTestVersion g = legalizeTestVersion $ \v -> + testVersionTemplate v + & versionCode .~ ChainwebVersionCode + (0x80000000 .|. graphToCodeN P1 (view chainGraphKnown g)) + & versionWindow .~ Nothing + & versionBlockRate .~ BlockRate 0 + & versionName .~ ChainwebVersionName ("test-" <> toText g) + & versionGraphs .~ End g + & versionCheats .~ Cheats + { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + , _disablePact = True + , _disableMempool = True + , _disablePeerValidation = True + } + & versionGenesis .~ ChainwebGenesis + { _genesisBlockPayload = AllChains emptyPayload + , _genesisBlockTarget = AllChains maxTarget + , _genesisTime = AllChains $ BlockCreationTime epoch + } + & versionForks .~ fastForks + & versionUpgrades .~ forkUpgrades v + [ (CoinV2, AllChains (upgrade Other.transactions)) + , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) + , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) + , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) + ] + +cpmTestVersion :: ChainGraph -> ChainwebVersion -> ChainwebVersion +cpmTestVersion g v = v + & versionWindow .~ Nothing + & versionBlockRate .~ BlockRate (Micros 200_000) + & versionGraphs .~ End g + & versionCheats .~ Cheats + { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + , _disablePact = False + , _disableMempool = False + , _disablePeerValidation = True + } + & versionGenesis .~ ChainwebGenesis + { _genesisBlockPayload = onChains $ + (unsafeChainId 0, TN0.payloadBlock) : + [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] + , _genesisBlockTarget = AllChains maxTarget + , _genesisTime = AllChains $ BlockCreationTime epoch + } + & versionUpgrades .~ chainZip HM.union + (forkUpgrades v + [ (CoinV2, AllChains (upgrade Other.transactions)) + , (Pact4Coin3, AllChains (Upgrade CoinV3.transactions True)) + , (Chainweb214Pact, AllChains (Upgrade CoinV4.transactions True)) + , (Chainweb215Pact, AllChains (Upgrade CoinV5.transactions True)) + ]) + (onChains [(unsafeChainId 3, HM.singleton (BlockHeight 2) (Upgrade MNKAD.transactions False))]) + +slowForkingCpmTestVersion :: ChainGraph -> ChainwebVersion +slowForkingCpmTestVersion g = legalizeTestVersion $ \v -> + cpmTestVersion g (testVersionTemplate v) + & versionName .~ ChainwebVersionName ("slowfork-CPM-test-" <> toText g) + & versionCode .~ ChainwebVersionCode + (0x80000003 .|. graphToCodeN P1 (view chainGraphKnown g)) + & versionForks .~ HM.fromList + [ (SlowEpoch, AllChains (BlockHeight 0)) + , (OldTargetGuard, AllChains (BlockHeight 0)) + , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) + , (OldDAGuard, AllChains (BlockHeight 0)) + , (Vuln797Fix, AllChains (BlockHeight 0)) + , (PactBackCompat_v16, AllChains (BlockHeight 0)) + , (SPVBridge, AllChains (BlockHeight 0)) + , (Pact44NewTrans, AllChains (BlockHeight 0)) + , (CoinV2, AllChains (BlockHeight 1)) + , (SkipTxTimingValidation, AllChains (BlockHeight 2)) + , (ModuleNameFix, AllChains (BlockHeight 2)) + , (ModuleNameFix2, AllChains (BlockHeight 2)) + , (Pact420, AllChains (BlockHeight 5)) + , (CheckTxHash, AllChains (BlockHeight 7)) + , (EnforceKeysetFormats, AllChains (BlockHeight 10)) + , (PactEvents, AllChains (BlockHeight 10)) + , (Pact4Coin3, AllChains (BlockHeight 20)) + , (Chainweb213Pact, AllChains (BlockHeight 26)) + , (Chainweb214Pact, AllChains (BlockHeight 30)) + , (Chainweb215Pact, AllChains (BlockHeight 35)) + , (Chainweb216Pact, AllChains (BlockHeight 53)) + , (Chainweb217Pact, AllChains (BlockHeight 55)) + ] + +fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion +fastForkingCpmTestVersion g = legalizeTestVersion $ \v -> + cpmTestVersion g (testVersionTemplate v) + & versionName .~ ChainwebVersionName ("fastfork-CPM-" <> toText g) + & versionCode .~ ChainwebVersionCode + (0x80000004 .|. graphToCodeN P1 (view chainGraphKnown g)) + & versionForks .~ fastForks + +noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion +noBridgeCpmTestVersion g = legalizeTestVersion $ \v -> + cpmTestVersion g (testVersionTemplate v) + & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) + & versionCode .~ ChainwebVersionCode + (0x80000005 .|. graphToCodeN P1 (view chainGraphKnown g)) + & versionForks .~ (fastForks & at SPVBridge .~ Just (AllChains maxBound)) + +timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion +timedConsensusVersion g1 g2 = legalizeTestVersion $ \v -> + testVersionTemplate v + & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) + & versionCode .~ ChainwebVersionCode + (foldl' (.|.) 0x80000001 + [ graphToCodeN P1 (view chainGraphKnown g1) + , graphToCodeN P2 (view chainGraphKnown g2) + ]) + & versionBlockRate .~ BlockRate 1_000_000 + & versionWindow .~ Nothing + & versionForks .~ HM.fromList + [ (Vuln797Fix, AllChains (BlockHeight 0)) + , (SkipTxTimingValidation, AllChains (BlockHeight 2)) + , (CheckTxHash, AllChains (BlockHeight 0)) + , (SlowEpoch, AllChains (BlockHeight 0)) + , (OldTargetGuard, AllChains (BlockHeight 0)) + , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) + , (OldDAGuard, AllChains (BlockHeight 0)) + ] + & versionUpgrades .~ AllChains HM.empty + & versionWindow .~ Nothing + & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) + & versionCheats .~ Cheats + { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + , _disablePact = True + , _disableMempool = True + , _disablePeerValidation = True + } + & versionGenesis .~ ChainwebGenesis + { _genesisBlockPayload = onChains $ + (unsafeChainId 0, TN0.payloadBlock) : + [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] + , _genesisBlockTarget = AllChains maxTarget + , _genesisTime = AllChains $ BlockCreationTime epoch + } \ No newline at end of file diff --git a/test/Chainweb/Test/Utils.hs b/test/Chainweb/Test/Utils.hs index bd330bef50..d49f030148 100644 --- a/test/Chainweb/Test/Utils.hs +++ b/test/Chainweb/Test/Utils.hs @@ -60,14 +60,9 @@ module Chainweb.Test.Utils , singleton , peterson , testBlockHeaderDbs -, petersonGenesisBlockHeaderDbs -, singletonGenesisBlockHeaderDbs , linearBlockHeaderDbs , starBlockHeaderDbs --- * Toy Server Interaction -, withChainServer - -- * Tasty TestTree Server and ClientEnv , testHost , TestClientEnv(..) @@ -122,6 +117,7 @@ module Chainweb.Test.Utils , ChainwebNetwork(..) , withNodes , withNodes_ +, awaitBlockHeight , runTestNodes , node , deadbeef @@ -133,6 +129,7 @@ module Chainweb.Test.Utils , withTime , withMVarResource , withEmptyMVarResource +, testRetryPolicy ) where import Control.Concurrent @@ -142,8 +139,9 @@ import Control.Exception (evaluate) #endif import Control.Lens import Control.Monad -import Control.Monad.Catch (MonadThrow, finally, bracket) +import Control.Monad.Catch (MonadThrow(..), finally, bracket) import Control.Monad.IO.Class +import Control.Retry import Data.Aeson (FromJSON, ToJSON) import Data.Bifunctor hiding (second) @@ -157,7 +155,7 @@ import qualified Data.Text.Encoding as T import Data.Tree import qualified Data.Tree.Lens as LT import qualified Data.Vector as V -import Data.Word (Word64) +import Data.Word import qualified Network.Connection as HTTP import qualified Network.HTTP.Client as HTTP @@ -169,7 +167,7 @@ import Network.Wai.Handler.WarpTLS as W (runTLSSocket) import Numeric.Natural -import Servant.Client (BaseUrl(..), ClientEnv, Scheme(..), mkClientEnv) +import Servant.Client (BaseUrl(..), ClientEnv, Scheme(..), mkClientEnv, runClientM) import System.Environment (withArgs) import System.IO @@ -194,7 +192,6 @@ import Data.List (sortOn,isInfixOf) import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis (genesisBlockHeader) import Chainweb.BlockHeaderDB import Chainweb.BlockHeaderDB.Internal import Chainweb.BlockHeight @@ -205,7 +202,9 @@ import Chainweb.Chainweb.ChainResources import Chainweb.Chainweb.Configuration import Chainweb.Chainweb.PeerResources import Chainweb.Crypto.MerkleLog hiding (header) +import Chainweb.Cut.CutHashes import Chainweb.CutDB +import Chainweb.CutDB.RestAPI.Client import Chainweb.Difficulty (targetToDifficulty) import Chainweb.Graph import Chainweb.HostAddress @@ -220,12 +219,13 @@ import Chainweb.Payload.PayloadStore import Chainweb.RestAPI import Chainweb.RestAPI.NetworkID import Chainweb.Test.P2P.Peer.BootstrapConfig - (bootstrapCertificate, bootstrapKey, bootstrapPeerConfig) + (testBootstrapCertificate, testBootstrapKey, testBootstrapPeerConfig) import Chainweb.Test.Utils.BlockHeader import Chainweb.Time import Chainweb.TreeDB import Chainweb.Utils import Chainweb.Utils.Serialization +import Chainweb.Test.TestVersions import Chainweb.Version import Chainweb.Version.Utils @@ -329,7 +329,7 @@ withInMemSQLiteResource = withSQLiteResource ":memory:" -- chainweb version! toyVersion :: ChainwebVersion -toyVersion = Test singletonChainGraph +toyVersion = barebonesTestVersion singletonChainGraph toyChainId :: ChainId toyChainId = someChainId toyVersion @@ -363,9 +363,8 @@ mockBlockFill = BlockFill mockBlockGasLimit mempty 0 genesisBlockHeaderForChain :: MonadThrow m => HasChainwebVersion v - => Integral i => v - -> i + -> Word32 -> m BlockHeader genesisBlockHeaderForChain v i = genesisBlockHeader (_chainwebVersion v) <$> mkChainId v maxBound i @@ -436,7 +435,7 @@ tree v g = do -- | Generate a sane, legal genesis block for 'Test' chainweb instance -- genesis :: ChainwebVersion -> Gen BlockHeader -genesis v = either (error . sshow) return $ genesisBlockHeaderForChain v (0 :: Int) +genesis v = either (error . sshow) return $ genesisBlockHeaderForChain v 0 forest :: Growth -> BlockHeader -> Gen (Forest BlockHeader) forest Randomly h = randomTrunk h @@ -478,7 +477,7 @@ header p = do :+: _chainId p :+: BlockWeight (targetToDifficulty target) + _blockWeight p :+: succ (_blockHeight p) - :+: v + :+: _versionCode v :+: epochStart (ParentHeader p) mempty t' :+: nonce :+: MerkleLogBody mempty @@ -509,14 +508,6 @@ testBlockHeaderDbs rdb v = mapM toEntry $ toList $ chainIds v d <- testBlockHeaderDb rdb (genesisBlockHeader v c) return (c, d) -petersonGenesisBlockHeaderDbs - :: RocksDb -> IO [(ChainId, BlockHeaderDb)] -petersonGenesisBlockHeaderDbs rdb = testBlockHeaderDbs rdb (Test petersonChainGraph) - -singletonGenesisBlockHeaderDbs - :: RocksDb -> IO [(ChainId, BlockHeaderDb)] -singletonGenesisBlockHeaderDbs rdb = testBlockHeaderDbs rdb (Test singletonChainGraph) - linearBlockHeaderDbs :: Natural -> IO [(ChainId, BlockHeaderDb)] @@ -545,33 +536,6 @@ starBlockHeaderDbs n genDbs = do newEntry i h = head $ testBlockHeadersWithNonce (Nonce i) h --- -------------------------------------------------------------------------- -- --- Toy Server Interaction - --- --- | Spawn a server that acts as a peer node for the purpose of querying / syncing. --- -withChainServer - :: forall t tbl a - . Show t - => ToJSON t - => FromJSON t - => CanReadablePayloadCas tbl - => ChainwebServerDbs t tbl - -> (ClientEnv -> IO a) - -> IO a -withChainServer dbs f = W.testWithApplication (pure app) work - where - app :: W.Application - app = chainwebApplication conf dbs - - work :: Int -> IO a - work port = do - mgr <- HTTP.newManager HTTP.defaultManagerSettings - f $ mkClientEnv mgr (BaseUrl Http "localhost" port "") - - conf = defaultChainwebConfiguration (Test singletonChainGraph) - -- -------------------------------------------------------------------------- -- -- Tasty TestTree Server and Client Environment @@ -615,12 +579,11 @@ pattern PayloadTestClientEnv { _pEnvClientEnv, _pEnvCutDb, _pEnvPayloadDbs, _eEn withTestAppServer :: Bool - -> ChainwebVersion -> IO W.Application -> (Int -> IO a) -> (a -> IO b) -> IO b -withTestAppServer tls v appIO envIO userFunc = bracket start stop go +withTestAppServer tls appIO envIO userFunc = bracket start stop go where warpOnException _ _ = return () start = do @@ -632,8 +595,8 @@ withTestAppServer tls v appIO envIO userFunc = bracket start stop go W.setBeforeMainLoop (putMVar readyVar ()) W.defaultSettings if | tls -> do - let certBytes = bootstrapCertificate v - let keyBytes = bootstrapKey v + let certBytes = testBootstrapCertificate + let keyBytes = testBootstrapKey let tlsSettings = tlsServerSettings certBytes keyBytes W.runTLSSocket tlsSettings settings sock app | otherwise -> @@ -653,12 +616,11 @@ withTestAppServer tls v appIO envIO userFunc = bracket start stop go -- withChainwebTestServer :: Bool - -> ChainwebVersion -> IO W.Application -> (Int -> IO a) -> (IO a -> TestTree) -> TestTree -withChainwebTestServer tls v appIO envIO test = withResource start stop $ \x -> +withChainwebTestServer tls appIO envIO test = withResource start stop $ \x -> test $ x >>= \(_, _, env) -> return env where start = do @@ -669,8 +631,8 @@ withChainwebTestServer tls v appIO envIO test = withResource start stop $ \x -> let settings = W.setBeforeMainLoop (putMVar readyVar ()) W.defaultSettings if | tls -> do - let certBytes = bootstrapCertificate v - let keyBytes = bootstrapKey v + let certBytes = testBootstrapCertificate + let keyBytes = testBootstrapKey let tlsSettings = tlsServerSettings certBytes keyBytes W.runTLSSocket tlsSettings settings sock app | otherwise -> @@ -697,7 +659,7 @@ clientEnvWithChainwebTestServer -> (IO (TestClientEnv t tbl) -> TestTree) -> TestTree clientEnvWithChainwebTestServer tls v dbsIO = - withChainwebTestServer tls v mkApp mkEnv + withChainwebTestServer tls mkApp mkEnv where mkApp :: IO W.Application mkApp = chainwebApplication (defaultChainwebConfiguration v) <$> dbsIO @@ -1027,6 +989,44 @@ withNodes -> TestTree withNodes = withNodes_ (genericLogger Warn print) +-- | Network initialization takes some time. Within my ghci session it took +-- about 10 seconds. Once initialization is complete even large numbers of empty +-- blocks were mined almost instantaneously. +-- +-- edtodo: move to RestAPI.Utils? +awaitBlockHeight + :: ChainwebVersion + -> (String -> IO ()) + -> IO ClientEnv + -> BlockHeight + -> IO () +awaitBlockHeight v step cenvIo i = do + cenv <- cenvIo + result <- retrying testRetryPolicy checkRetry + $ const $ runClientM (cutGetClient v) cenv + case result of + Left e -> throwM e + Right x + | all (\bh -> _bhwhHeight bh >= i) (_cutHashes x) -> return () + | otherwise -> error + $ "retries exhausted: waiting for cut height " <> sshow i + <> " but only got " <> sshow (_cutHashesHeight x) + where + checkRetry s (Left e) = do + step $ "awaiting cut of height " <> show i + <> ". No result from node: " <> show e + <> " [" <> show (view rsIterNumberL s) <> "]" + return True + checkRetry s (Right c) + | all (\bh -> _bhwhHeight bh >= i) (_cutHashes c) = return False + | otherwise = do + step + $ "awaiting cut with all block heights >= " <> show i + <> ". Current cut height: " <> show (_cutHashesHeight c) + <> ". Current block heights: " <> show (_bhwhHeight <$> _cutHashes c) + <> " [" <> show (view rsIterNumberL s) <> "]" + return True + runTestNodes :: Logger logger => B.ByteString @@ -1118,7 +1118,7 @@ bootstrapConfig conf = conf & set (configP2p . p2pConfigPeer) peerConfig & set (configP2p . p2pConfigKnownPeers) [] where - peerConfig = head (bootstrapPeerConfig $ _configChainwebVersion conf) + peerConfig = head (testBootstrapPeerConfig $ _configChainwebVersion conf) & set peerConfigPort 0 & set peerConfigHost host @@ -1150,3 +1150,14 @@ withTime = withResource getCurrentTimeIntegral mempty withEmptyMVarResource :: (IO (MVar a) -> TestTree) -> TestTree withEmptyMVarResource = withResource newEmptyMVar mempty + +-- | Backoff up to a constant 250ms, limiting to ~40s +-- (actually saw a test have to wait > 22s) +testRetryPolicy :: RetryPolicy +testRetryPolicy = stepped <> limitRetries 150 + where + stepped = retryPolicy $ \rs -> case rsIterNumber rs of + 0 -> Just 20_000 + 1 -> Just 50_000 + 2 -> Just 100_000 + _ -> Just 250_000 diff --git a/test/Chainweb/Test/Utils/ApiQueries.hs b/test/Chainweb/Test/Utils/ApiQueries.hs index 917424f53e..4bf1ce78e9 100644 --- a/test/Chainweb/Test/Utils/ApiQueries.hs +++ b/test/Chainweb/Test/Utils/ApiQueries.hs @@ -48,10 +48,10 @@ import Chainweb.Version -- -------------------------------------------------------------------------- -- -- Endpoints -endpoint :: HasCallStack => HTTP.Manager -> ChainwebVersion -> HTTP.ClientEnv +endpoint :: HasCallStack => HTTP.Manager -> ChainwebVersionName -> HTTP.ClientEnv endpoint mgr Mainnet01 = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us-e1.chainweb.com" 443 "" endpoint mgr Testnet04 = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us1.testnet.chainweb.com" 443 "" -endpoint mgr Development = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us1.tn.chainweb.com" 443 "" +endpoint mgr (Development ()) = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us1.tn.chainweb.com" 443 "" endpoint _ x = error $ "endpoint: unsupported chainweb version " <> sshow x -- -------------------------------------------------------------------------- -- @@ -65,7 +65,7 @@ mkMgr = HTTP.newTlsManagerWith $ HTTP.mkManagerSettings runQuery :: HasCallStack => HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionTag -> HTTP.ClientM a -> IO a runQuery mgr v q = HTTP.runClientM q (endpoint mgr v) >>= \case @@ -78,7 +78,7 @@ runQuery mgr v q = HTTP.runClientM q (endpoint mgr v) >>= \case getHeaderByHash :: HasCallStack => HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionTag -> ChainId -> BlockHash -> IO BlockHeader @@ -87,7 +87,7 @@ getHeaderByHash mgr v c = runQuery mgr v . headerClient v c getHeaderByHeight :: HasCallStack => HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionTag -> ChainId -> BlockHeight -> IO BlockHeader @@ -110,7 +110,7 @@ getHeaderByHeight mgr v cid height = do currentHash :: HasCallStack => HTTP.Manager - -> ChainwebVersion + -> ChainwebVersionTag -> ChainId -> IO BlockHashWithHeight currentHash mgr v cid = do diff --git a/test/Chainweb/Test/Utils/BlockHeader.hs b/test/Chainweb/Test/Utils/BlockHeader.hs index 32532424b0..ebfcc485be 100644 --- a/test/Chainweb/Test/Utils/BlockHeader.hs +++ b/test/Chainweb/Test/Utils/BlockHeader.hs @@ -39,7 +39,6 @@ import GHC.Stack import Chainweb.BlockCreationTime import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.ChainValue import Chainweb.Payload import Chainweb.Time diff --git a/test/Chainweb/Test/Utils/TestHeader.hs b/test/Chainweb/Test/Utils/TestHeader.hs index f9535b73e0..d76dca20dd 100644 --- a/test/Chainweb/Test/Utils/TestHeader.hs +++ b/test/Chainweb/Test/Utils/TestHeader.hs @@ -26,8 +26,8 @@ module Chainweb.Test.Utils.TestHeader , testHeaderChainLookup , genesisTestHeader , genesisTestHeaders -, queryTestHeader -, queryTestHeaderByHeight +-- , queryTestHeader +-- , queryTestHeaderByHeight , arbitraryTestHeader , arbitraryTestHeaderHeight ) where @@ -54,11 +54,10 @@ import Test.QuickCheck.Gen (Gen) import Chainweb.BlockCreationTime import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis import Chainweb.BlockHeight import Chainweb.ChainValue import Chainweb.Test.Orphans.Internal -import Chainweb.Test.Utils.ApiQueries +-- import Chainweb.Test.Utils.ApiQueries import Chainweb.Version import Chainweb.Storage.Table @@ -204,54 +203,54 @@ genesisTestHeader v cid = TestHeader where gen = genesisBlockHeader (_chainwebVersion v) (_chainId cid) --- -------------------------------------------------------------------------- -- --- Query TestHeader from a network - -queryTestHeader - :: HasCallStack - => HasChainwebVersion v - => HasChainId c - => v - -> c - -> BlockHash - -> IO TestHeader -queryTestHeader v c h = do - mgr <- mkMgr - hdr <- getHeaderByHash mgr ver cid h - parent <- getHeaderByHash mgr ver cid $ _blockParent hdr - ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr ver ac a) - $ _getBlockHashRecord - $ _blockAdjacentHashes hdr - return $ TestHeader - { _testHeaderHdr = hdr - , _testHeaderParent = ParentHeader parent - , _testHeaderAdjs = toList ads - } - where - ver = _chainwebVersion v - cid = _chainId c - -queryTestHeaderByHeight - :: HasCallStack - => HasChainwebVersion v - => HasChainId c - => v - -> c - -> BlockHeight - -> IO TestHeader -queryTestHeaderByHeight v c h = do - mgr <- mkMgr - hdr <- getHeaderByHeight mgr ver cid h - parent <- getHeaderByHash mgr ver cid $ _blockParent hdr - ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr ver ac a) - $ _getBlockHashRecord - $ _blockAdjacentHashes hdr - return $ TestHeader - { _testHeaderHdr = hdr - , _testHeaderParent = ParentHeader parent - , _testHeaderAdjs = toList ads - } - where - ver = _chainwebVersion v - cid = _chainId c +-- -- -------------------------------------------------------------------------- -- +-- -- Query TestHeader from a network + +-- queryTestHeader +-- :: HasCallStack +-- => HasChainwebVersion v +-- => HasChainId c +-- => v +-- -> c +-- -> BlockHash +-- -> IO TestHeader +-- queryTestHeader v c h = do +-- mgr <- mkMgr +-- hdr <- getHeaderByHash mgr (chainwebVersionTag ver) cid h +-- parent <- getHeaderByHash mgr (chainwebVersionTag ver) cid $ _blockParent hdr +-- ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr (chainwebVersionTag ver) ac a) +-- $ _getBlockHashRecord +-- $ _blockAdjacentHashes hdr +-- return $ TestHeader +-- { _testHeaderHdr = hdr +-- , _testHeaderParent = ParentHeader parent +-- , _testHeaderAdjs = toList ads +-- } +-- where +-- ver = _chainwebVersion v +-- cid = _chainId c + +-- queryTestHeaderByHeight +-- :: HasCallStack +-- => HasChainwebVersion v +-- => HasChainId c +-- => v +-- -> c +-- -> BlockHeight +-- -> IO TestHeader +-- queryTestHeaderByHeight v c h = do +-- mgr <- mkMgr +-- hdr <- getHeaderByHeight mgr ver cid h +-- parent <- getHeaderByHash mgr ver cid $ _blockParent hdr +-- ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr ver ac a) +-- $ _getBlockHashRecord +-- $ _blockAdjacentHashes hdr +-- return $ TestHeader +-- { _testHeaderHdr = hdr +-- , _testHeaderParent = ParentHeader parent +-- , _testHeaderAdjs = toList ads +-- } +-- where +-- ver = _chainwebVersion v +-- cid = _chainId c diff --git a/test/Chainweb/Test/Version.hs b/test/Chainweb/Test/Version.hs index 224b93fc90..bc49d51f5b 100644 --- a/test/Chainweb/Test/Version.hs +++ b/test/Chainweb/Test/Version.hs @@ -1,6 +1,10 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -Wno-missing-fields #-} -- | -- Module: Chainweb.Test.Version @@ -27,13 +31,17 @@ import Test.Tasty.QuickCheck (testProperty) import Chainweb.BlockHash import Chainweb.BlockHeader -import Chainweb.BlockHeader.Genesis +import Chainweb.BlockHeight import Chainweb.Graph import Chainweb.Test.Orphans.Internal import Chainweb.Test.Orphans.Internal () import Chainweb.Utils +import Chainweb.Utils.Rule import Chainweb.Utils.Serialization import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet tests :: TestTree tests = testGroup "ChainwebVersion properties" @@ -57,30 +65,26 @@ propForVersions desc prop = testGroup desc graphTests :: TestTree graphTests = testGroup "Graphs" - [ propForVersions "chainwebGraphs are sorted" prop_chainGraphs_sorted - , propForVersions "chainGraphs history starts at 0" $ prop_chainGraphs_0 - , propForVersions "gensisHeight is greater or equal than 0 for all chains" prop_genesisHeight + [ propForVersions "versionGraphs are sorted" prop_chainGraphs_sorted + , propForVersions "genesisHeight is greater or equal than 0 for all chains" prop_genesisHeight , propForVersions "chain graphs order ordered by order" prop_chainGraphs_order , propForVersions "chainIds are chains of latest graph" prop_chainIds ] prop_chainGraphs_sorted :: ChainwebVersion -> Property prop_chainGraphs_sorted v - = (NE.reverse $ NE.sort $ chainwebGraphs v) === chainwebGraphs v - -prop_chainGraphs_0 :: ChainwebVersion -> Property -prop_chainGraphs_0 = (===) 0 . fst . NE.last . chainwebGraphs + = property (ruleValid (_versionGraphs v)) prop_chainGraphs_order :: ChainwebVersion -> Property prop_chainGraphs_order v = orders === NE.reverse (NE.sort orders) where - orders = fmap (order . snd) $ chainwebGraphs v + orders = ruleElems (BlockHeight 0) $ fmap order $ _versionGraphs v prop_genesisHeight :: ChainwebVersion -> Property prop_genesisHeight v = property $ all ((>= 0) . genesisHeight v) $ chainIds v prop_chainIds :: ChainwebVersion -> Property -prop_chainIds v = chainIds v === graphChainIds (snd $ NE.head $ chainwebGraphs v) +prop_chainIds v = chainIds v === graphChainIds (snd $ ruleHead $ _versionGraphs v) -- -------------------------------------------------------------------------- -- -- Header Sizes @@ -90,7 +94,6 @@ headerSizeTests = testGroup "HeaderSize" [ propForVersions "base size golden" prop_headerBaseSizeBytes_golden , propForVersions "base size" prop_headerBaseSizeBytes , propForVersions "sizes sorted" prop_headerSizes_sorted - , propForVersions "sizes 0" prop_headerSizes_0 , propForVersions "sizes order" prop_headerSizes_order , propForVersions "genesis header size bytes" prop_headerSizeBytes_gen , propForVersions "header size bytes" prop_headerSizeBytes @@ -101,7 +104,7 @@ headerSizeTests = testGroup "HeaderSize" -- be manually updated. This protectes against accidentally changing this value. -- prop_headerBaseSizeBytes_golden :: ChainwebVersion -> Property -prop_headerBaseSizeBytes_golden v = headerBaseSizeBytes v === 208 +prop_headerBaseSizeBytes_golden v = _versionHeaderBaseSizeBytes v === 208 prop_headerBaseSizeBytes :: ChainwebVersion -> Property prop_headerBaseSizeBytes v = property $ do @@ -109,19 +112,16 @@ prop_headerBaseSizeBytes v = property $ do let genHdr = genesisBlockHeader v cid gen = runPutS $ encodeBlockHeader genHdr as = runPutS $ encodeBlockHashRecord (_blockAdjacentHashes genHdr) - return $ headerBaseSizeBytes v === int (B.length gen - B.length as) + return $ _versionHeaderBaseSizeBytes v === int (B.length gen - B.length as) prop_headerSizes_sorted :: ChainwebVersion -> Property prop_headerSizes_sorted v - = (NE.reverse $ NE.sort $ headerSizes v) === headerSizes v - -prop_headerSizes_0 :: ChainwebVersion -> Property -prop_headerSizes_0 = (===) 0 . fst . NE.last . headerSizes + = NE.reverse (NE.sort (ruleElems (BlockHeight 0) (headerSizes v))) === ruleElems (BlockHeight 0) (headerSizes v) prop_headerSizes_order :: ChainwebVersion -> Property prop_headerSizes_order v = orders === NE.reverse (NE.sort orders) where - orders = fmap (order . snd) $ chainwebGraphs v + orders = ruleElems (BlockHeight 0) $ fmap order $ _versionGraphs v prop_headerSizeBytes_gen :: ChainwebVersion -> Property prop_headerSizeBytes_gen v = property $ do diff --git a/test/SlowTests.hs b/test/SlowTests.hs index d484a24627..b381189db6 100644 --- a/test/SlowTests.hs +++ b/test/SlowTests.hs @@ -20,6 +20,7 @@ import Test.Tasty import Chainweb.Graph import qualified Chainweb.Test.MultiNode +import Chainweb.Test.TestVersions import Chainweb.Version import qualified Network.X509.SelfSigned.Test @@ -32,8 +33,8 @@ loglevel = Warn suite :: TestTree suite = testGroup "ChainwebSlowTests" - [ Chainweb.Test.MultiNode.test loglevel (TimedConsensus petersonChainGraph twentyChainGraph) 10 120 - , Chainweb.Test.MultiNode.replayTest loglevel (FastTimedCPM pairChainGraph) 6 + [ Chainweb.Test.MultiNode.test loglevel (timedConsensusVersion petersonChainGraph twentyChainGraph) 10 30 + , Chainweb.Test.MultiNode.replayTest loglevel (fastForkingCpmTestVersion pairChainGraph) 6 , testGroup "Network.X05.SelfSigned.Test" [ Network.X509.SelfSigned.Test.tests ] diff --git a/test/golden/development-block-hashes-expected.txt b/test/golden/development-block-hashes-expected.txt index 79e04e8926..2b2864c50d 100644 --- a/test/golden/development-block-hashes-expected.txt +++ b/test/golden/development-block-hashes-expected.txt @@ -1 +1 @@ -I44qr9WzXAwKaBL_cOdhLC9PKB0Jmo0dIuMXUz3Q4ws=LHOg2z-LlJYKpbPqyPtmmPtt52TsJZw98-8Yqa6z3mY=tMZtszaMwDE8ZBkZ63wbTmuL3R32bH1pVH6BCxO-KTA=KzOGhVdvZrAJnsyd94qVrt2y9JelNFGI36k5qzFPovM=Dgkcz1vTanUkii3ytBTjxospbVctGauWbFITQTf_7RU=QBgdPHASVl5DMiTTkCpo9aYzq5BcuQOAjNNh4ddfOaA=ul5HX4I4gzLgiT5ZIcv6to9vd0NieNiF0gtXV56u9aQ=vcCyL7Q3hrNMdIH3ugomHcKgzkzKIC9-53X_sSiGpm0=dglsXm0bcrHzXSPnAbRGmSMrSxjR8hgecb9tuHmI9o4=Uqb6NvnmsDKUf1nRKNB6ogZVQGT75lebfyHPMgq6IYk=pKCVftcctJ0SM_3h9vTuMKFo-eDnZB9qVnnpgMzW5Hg=3RPSalWuswFQJegbeEvrizAeQC6wxUJ9UtqM3xMkZBE=RpN2i3kNWkh0Dm7gLvhX3YlkruLXA-s4La5W831CRao=KwQ8VlmijrLd8diO5Ipy2H89hKhKDJQ3TC3al7vRrdQ=IfaxAeTGndKZqBb_0emJlkBYt4r5x-h8AFTH-hFjQKk=KMgFuiEt8hVL5FjjXw4pRmjI5FtgZ0W7loGRkOBZP5Y=_agbXYJt3-uklho_gSJBgIxxyHLMy_BtV_cvObxvmUY=C9luSkCJOC6g8lpKzDD56YJuKqQQLXR3PbOaPEgTIwA=ftB18G_4NdWDC6LYulkocXWPAkThii9nvsEDsP9Lzk4=GWe3oNkU1tWHvW_qzJGJ1eS5g-6wZRIIzhj25oF5Tvk= \ No newline at end of file +I44qr9WzXAwKaBL_cOdhLC9PKB0Jmo0dIuMXUz3Q4ws=LHOg2z-LlJYKpbPqyPtmmPtt52TsJZw98-8Yqa6z3mY=tMZtszaMwDE8ZBkZ63wbTmuL3R32bH1pVH6BCxO-KTA=KzOGhVdvZrAJnsyd94qVrt2y9JelNFGI36k5qzFPovM=Dgkcz1vTanUkii3ytBTjxospbVctGauWbFITQTf_7RU=QBgdPHASVl5DMiTTkCpo9aYzq5BcuQOAjNNh4ddfOaA=ul5HX4I4gzLgiT5ZIcv6to9vd0NieNiF0gtXV56u9aQ=vcCyL7Q3hrNMdIH3ugomHcKgzkzKIC9-53X_sSiGpm0=dglsXm0bcrHzXSPnAbRGmSMrSxjR8hgecb9tuHmI9o4=Uqb6NvnmsDKUf1nRKNB6ogZVQGT75lebfyHPMgq6IYk=QH7IwZf9tKCvimg4ixGeIGAyDm8HMDYrUas3jonqvYw=EC8nr6z0h4VUkAXRv5OlbzqEZa2svQZTj1oaJUMwRUg=tTgE2LZFcibxVnv00im0m3V7UYY9-taN615DPc1Y6dY=ezUdXnVjpEleQziJLVWK19n5_LWClh4ya7JRwusFnZQ=VXhjzpyPWl4Qniho1abLTOx2UZ-BFGGF2iWi4v6hTfQ=iNKy0Kw3PPCktOmU5xUhNekzq67al061oYtcHhNJq_E=QDvloZ__EySOGhpx64zGmUkjDvQU-Q1PFb_G7_8cesw=Qt-GUJrJGjjl0r-PWJcTa_VLzVxpCCl6SqaSeEIA274=6ds_b1BAaJHDL5WCdf-8XewDMIzswRvmzhtXtk8KprM=xpupUzKhexojr5gz8HcEkY34VRRfjA4eR5cjxTXmh78= \ No newline at end of file diff --git a/test/golden/devnet-block-hashes-expected.txt b/test/golden/devnet-block-hashes-expected.txt new file mode 100644 index 0000000000..0e250c30b7 --- /dev/null +++ b/test/golden/devnet-block-hashes-expected.txt @@ -0,0 +1 @@ +70ANlh0V8QWWYCAxcB7X8WJUEgyhv_DrToI2l5O-bIc=GlP1_wByw_zA_QMGVUx4RRUjrqssa9gaOP7Shf9NGsY=H5NPR54a9-_rr3XAR6a-0okJ4y6AaZ9UBwmHbn97-3s=fd6ShZhaDLkEIDTAwSa7UEtmFlg1fZPdrw1g3-1jfwk=qDJC2lvVNX72gN8YT4_SqZUjjUOzM-b-wC4F74Oueeo=Mo4r_lWaMVKL_6XK8IHuU_RIMlGLGRhUm_1ZY9hYXcM=khxq-nZGoEar8aYNsqWMPPEMVTTzqs_svobyiFYLrvg=ZAQX1JKbN2RmUFVx_aG87EwxwTic23DKo_CQb9G95pU=wWUUEitrmA-JUMs-MYHfjrfXgR0eB9vetLTZV2XtD6k=xRrZrZqYND00wwEFyzqN2VqsvnZgQD0Kkc9-h5RpDoQ=K13M0IcaOdQ4Q47ZDWh1OoZx0OG6_yYNoUnuDRxMkPk=Ucw9OOF8ui43ADW2swEvLNqFm2Ip2kAQ2II_Lfv3Efo=tW8NGv1_f7q4ulyJzNpNVIz1umI28LSqr175J73cPks=26bRDnlZnb_aFGKq99G0Xrv1MAmlNMxtAZBiPAn0Rms=4Gfi64V0wmMDcuzfXHq7fmZrxNRrf82-PvFCvoDVIlw=ppEFtdpd82-SYVo2T8NJb1gVLiAF46bqtGizaxngmYA=lI4BBJRKw0C8l5nXm_kq1Wt-G-Kx-pgc5S-BA6NwTMg=BAPCD9JBJEw51_guGGpGCHnzrcG6WOm2VoBAj_rzLNE=HxgFA-iCrxGCrfCCryDZVZzxaq8uNTVw0I8MMr2-HXk=KS20VqEfuc58N_y5EBPllmLHj-byluevh4YpvTKaiNM= \ No newline at end of file diff --git a/test/golden/empty-block-tests-expected.txt b/test/golden/empty-block-tests-expected.txt index 5f26ac6471..fa4e2d9671 100644 --- a/test/golden/empty-block-tests-expected.txt +++ b/test/golden/empty-block-tests-expected.txt @@ -3,6 +3,6 @@ results: transactions: [] minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119 transactionsHash: lL9ztEiU-NwzrlTpBbvhT4M1l5Shsht94OwFyhBaFD0 - outputsHash: WGRU7eVVlp2OTKArZtfxxn-qnbYQJ2RWyc6GbsGOGHY - payloadHash: e6jmUlHM4fsm6pK61sU4DLjlLJpd4DpclOdkjQjxQys - coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJamhZT0ZsMlQxaDZTM1I1UjJvdFVuWkNSRFZEYWtkQlkyMTBXVlJNYVRod1VrSmFWekJ1ZDBwbmJVMGkiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 + outputsHash: fb3bO384oIyom069n0hV7P8L-Hnrk5tKvKFW-yHMMT0 + payloadHash: TB6Wg1ztCYu86QOVESUYe_S45HCDRHqBFznzmwpEx9c + coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa041UkVSck1YRTRTVjh3VkZoTVlVNXFWRTVTV1dWTWJYVm9WbHBNY1RSZk9XOVNPR0pCYVd0WFkwa2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 diff --git a/test/golden/new-block-0-expected.txt b/test/golden/new-block-0-expected.txt index 17b0733986..8645cdd1b8 100644 --- a/test/golden/new-block-0-expected.txt +++ b/test/golden/new-block-0-expected.txt @@ -10,7 +10,7 @@ results: - - eyJoYXNoIjoicFBrLWxFUE1jRnRDWXdXR2ZaejV2bGdzZ1ZYV3dYbXhmQVJ6bkFCbUVONCIsInNpZ3MiOlt7InNpZyI6IjM3YjI4MmY1YWQ0YWY0Yzk2YmE1NzA5YWEzOTI2NTU1YjU5MDEwYzczZmVmZTUwNzQyMGYyODVjZTI1MmQ5ZjM1YzQ5NjAxYTgwMDYxZGU2NzEzNTcxNzY2MTNmODIyODQxMDU0MjliZTVkOTEzNWMzODAxMzcyN2UyMmI3ZjA2In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihmcmVlLnRlc3QxLnRyYW5zZmVyIFxcXCJBY2N0MVxcXCIgXFxcIkFjY3QyXFxcIiAxLjAwKVwifX0sXCJzaWduZXJzXCI6W3tcInB1YktleVwiOlwiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwifV0sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjEwMDAwMDAsXCJnYXNMaW1pdFwiOjEwMDAwLFwiY2hhaW5JZFwiOlwiMFwiLFwiZ2FzUHJpY2VcIjoxLjBlLTIsXCJzZW5kZXJcIjpcInNlbmRlcjAwXCJ9LFwibm9uY2VcIjpcIjEzXCJ9In0 - eyJnYXMiOjQwNywicmVzdWx0Ijp7InN0YXR1cyI6InN1Y2Nlc3MiLCJkYXRhIjoiV3JpdGUgc3VjY2VlZGVkIn0sInJlcUtleSI6InBQay1sRVBNY0Z0Q1l3V0dmWno1dmxnc2dWWFd3WG14ZkFSem5BQm1FTjQiLCJsb2dzIjoib3hWd2tvU21xM2JZeVh5aHYwT0V3ZTN2c0tGbWlkRTBtTEotQ2FZWVhRayIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjIwfQ - - eyJoYXNoIjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsInNpZ3MiOlt7InNpZyI6IjZhODRiMWRkNGFkNDE5ZGY3YThhODA4NGYwNWEyZDM5YzExNTBmMjI5ZTM3OTFlM2UwY2E0OTkyZDkwNGNhOGVlMTk4MTljNTE3YTVjNjM5M2E3ZmYyNWI1NjdiNjJjOTYwZTFiYmI4ZTE2YjZiMWU2YmRkNDU1Njk3ODc1MDA5In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAncHJldi1ibG9jay1oYXNoIChjaGFpbi1kYXRhKSlcIn19LFwic2lnbmVyc1wiOlt7XCJwdWJLZXlcIjpcIjM2ODgyMGY4MGMzMjRiYmM3YzJiMDYxMDY4OGE3ZGE0M2UzOWY5MWQxMTg3MzI2NzFjZDljNzUwMGZmNDNjY2FcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxMDAwMDAwLFwiZ2FzTGltaXRcIjoxMDAwMCxcImNoYWluSWRcIjpcIjBcIixcImdhc1ByaWNlXCI6MS4wZS0yLFwic2VuZGVyXCI6XCJzZW5kZXIwMFwifSxcIm5vbmNlXCI6XCIxNFwifSJ9 - - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6InNWMDlUbHhfZURIQWdYZkY1WS1jcG1MMVBTWFFsTERKRWc5MUJKczBieWMifSwicmVxS2V5IjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsImxvZ3MiOiJUVVVlWFJmRFlnQlZTdjFZU0ktU3lfRVdtcHRaWXZqQlNmY1ZMc2ZzcVpVIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MjN9 + - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6ImZyS2FhZ2dyRHdKeE1CTWlPVDgyblZnNzRueXdWYlNrM1BfT0tCTVNtV28ifSwicmVxS2V5IjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsImxvZ3MiOiJUVVVlWFJmRFlnQlZTdjFZU0ktU3lfRVdtcHRaWXZqQlNmY1ZMc2ZzcVpVIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MjN9 - - eyJoYXNoIjoiTzhoWU1TVUhnSkNPRXFoTy0yRFRNRGJhTzBIZmx6VjM0UHRzM0dlUHBJUSIsInNpZ3MiOlt7InNpZyI6ImMxYjQ5OTQwNTQ0MjZmNzE2NWM2NmZjNjY0MzE0N2M1N2QxYTczYzM3NjEwMjU4OGU0M2QyMzA1ZjBhY2FhYTQyMDRkZGRmNGRhMWFiMzk0OTdkY2FiM2FjZWU1NmQ0NmQ3NzJkNDZkNjAyNmU5ZjQ4MGFmMWNiMWIyNWFmYzA3In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpXCJ9fSxcInNpZ25lcnNcIjpbe1wicHViS2V5XCI6XCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJ9XSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTAwMDAwMCxcImdhc0xpbWl0XCI6MTAwMDAsXCJjaGFpbklkXCI6XCIwXCIsXCJnYXNQcmljZVwiOjEuMGUtMixcInNlbmRlclwiOlwic2VuZGVyMDBcIn0sXCJub25jZVwiOlwiMTVcIn0ifQ - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJ0aW1lIjoiMTk3MC0wMS0wMVQwMDowMDowMFoifX0sInJlcUtleSI6Ik84aFlNU1VIZ0pDT0VxaE8tMkRUTURiYU8wSGZselYzNFB0czNHZVBwSVEiLCJsb2dzIjoidTdKNDU3dGdzNDJ1dGhjOU1TbWtkaTkxT2tUb2FCNFpGei1QUWtSajJhdyIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjI2fQ - - eyJoYXNoIjoiSGFsbTJLYUV5Qm5weXJqMkxHMmJQcVZDUmZBYk8yWHZzZWlnaURaZElLOCIsInNpZ3MiOlt7InNpZyI6ImI0MDNlZWI3ODI0NzA0OWU0NjEzNGQzYWQ0MWZlMTAwNWMxM2I0NzNhNjUzZGNmNTVlN2ZmMzIzOGU5N2VkYmRmZTMyZmFmODczODViYTc0MmNmMDFmYmFkMDU2ODEwNWVjNDhhMDk1MzIyNWJmMmY0ODJjNjhiMjkyZGNmOTAzIn1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAnYmxvY2staGVpZ2h0IChjaGFpbi1kYXRhKSlcIn19LFwic2lnbmVyc1wiOlt7XCJwdWJLZXlcIjpcIjM2ODgyMGY4MGMzMjRiYmM3YzJiMDYxMDY4OGE3ZGE0M2UzOWY5MWQxMTg3MzI2NzFjZDljNzUwMGZmNDNjY2FcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxMDAwMDAwLFwiZ2FzTGltaXRcIjoxMDAwMCxcImNoYWluSWRcIjpcIjBcIixcImdhc1ByaWNlXCI6MS4wZS0yLFwic2VuZGVyXCI6XCJzZW5kZXIwMFwifSxcIm5vbmNlXCI6XCIxNlwifSJ9 @@ -25,6 +25,6 @@ results: - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6InNlbmRlcjAwIn0sInJlcUtleSI6IldnbnVDZzZMX2w2bHpiald0QmZNRXVQdHR5X3VHY05yVW9sNUhHUkVPX28iLCJsb2dzIjoiVl96OG10RjdtMVpNRWJiQUNtZlFLbWFLejdhZF9qWjhYbXhaYV9KMTRWWSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjQxfQ minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119 transactionsHash: jI_aVcPfGdURmGMHOnzx9YdhzAQBvRZVLKQdpmd_9BQ - outputsHash: siboKtiKPsLsxHAlZ7c5xqfPRL6sZkgMNtINmbNmsXQ - payloadHash: lXGcKlZWurfk1orxlGgahlaDoX-1mAVPRW9pMHarbP0 - coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJamhZT0ZsMlQxaDZTM1I1UjJvdFVuWkNSRFZEYWtkQlkyMTBXVlJNYVRod1VrSmFWekJ1ZDBwbmJVMGkiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 + outputsHash: O2u6JQsu3zygT_2GH5KHWF7izbiD_ZWdk9rO0SdkMWQ + payloadHash: kfMV0MFKedYBe40R9_0EUMrSRDcQ9dtsooAjA2K1KWE + coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa041UkVSck1YRTRTVjh3VkZoTVlVNXFWRTVTV1dWTWJYVm9WbHBNY1RSZk9XOVNPR0pCYVd0WFkwa2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 diff --git a/tools/cwtool/TxSimulator.hs b/tools/cwtool/TxSimulator.hs index e3a99148e2..ba57067aa0 100644 --- a/tools/cwtool/TxSimulator.hs +++ b/tools/cwtool/TxSimulator.hs @@ -49,6 +49,8 @@ import Chainweb.Transaction import Chainweb.Utils import Chainweb.Utils.Paging import Chainweb.Version +import Chainweb.Version.Mainnet +import Chainweb.Version.Registry import Network.Connection import Network.HTTP.Client.TLS @@ -115,8 +117,6 @@ simulate sc@(SimConfig dbDir txIdx' _ _ cid ver) = do evalPactServiceM pss pse $ doBlock True parent (zip hdrs pwos) - - where cwLogger = genericLogger Debug T.putStrLn @@ -158,7 +158,7 @@ spvSim sc bh pwo = do go mv cp = modifyMVar mv $ searchOuts cp searchOuts _ [] = return ([],Left "spv: proof not found") searchOuts cp@(ContProof pf) ((Transaction ti,TransactionOutput _o):txs) = - case codecDecode (chainwebPayloadCodec (Just (scVersion sc,_blockHeight bh))) ti of + case codecDecode (chainwebPayloadCodec (pactParserVersion (scVersion sc) (_chainId bh) (_blockHeight bh))) ti of Left {} -> internalError "input decode failed" Right cmd -> case _pPayload $ payloadObj $ _cmdPayload cmd of Continuation cm | _cmProof cm == Just cp -> do @@ -206,7 +206,7 @@ fetchOutputs sc cenv bhs = do simulateMain :: IO () simulateMain = do execParser opts >>= \(d,s,e,i,h,c,v) -> do - vv <- chainwebVersionFromText (T.pack v) + vv <- findKnownVersion $ ChainwebVersionName (T.pack v) cc <- chainIdFromText (T.pack c) u <- parseBaseUrl h let rng = (fromIntegral @Integer s,fromIntegral @Integer (fromMaybe s e)) diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index b1ae827942..784dfbdfd6 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -27,7 +27,7 @@ module Ea ( main ) where -import Control.Lens (set) +import Control.Lens import Data.Foldable import Data.Functor @@ -60,7 +60,7 @@ import Chainweb.Time import Chainweb.Transaction (ChainwebTransaction, chainwebPayloadCodec, mkPayloadWithTextOld) import Chainweb.Utils -import Chainweb.Version (ChainwebVersion(..)) +import Chainweb.Version import Chainweb.Version.Utils (someChainId) import Chainweb.Storage.Table.RocksDB @@ -186,7 +186,7 @@ genPayloadModule' v tag cwTxs = pdb <- newPayloadDb withSystemTempDirectory "ea-pact-db" $ \pactDbDir -> do T2 payloadWO _ <- withSqliteDb cid logger pactDbDir False $ \env -> - runPactService' v cid logger bhdb pdb env defaultPactServiceConfig $ + withPactService v cid logger bhdb pdb env defaultPactServiceConfig $ execNewGenesisBlock noMiner (V.fromList cwTxs) let payloadYaml = TE.decodeUtf8 $ Yaml.encode payloadWO @@ -280,7 +280,7 @@ genTxModule tag txFiles = do let encTxs = map quoteTx cwTxs quoteTx tx = " \"" <> encTx tx <> "\"" - encTx = encodeB64UrlNoPaddingText . codecEncode (chainwebPayloadCodec Nothing) + encTx = encodeB64UrlNoPaddingText . codecEncode (chainwebPayloadCodec maxBound) modl = T.unlines $ startTxModule tag <> [T.intercalate "\n ,\n" encTxs] <> endTxModule fileName = "src/Chainweb/Pact/Transactions/" <> tag <> "Transactions.hs" @@ -302,7 +302,7 @@ startTxModule tag = , "transactions :: IO [ChainwebTransaction]" , "transactions =" , " let decodeTx t =" - , " fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec Nothing) =<< decodeB64UrlNoPaddingText t" + , " fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t" , " in mapM decodeTx [" ] diff --git a/tools/ea/Ea/Genesis.hs b/tools/ea/Ea/Genesis.hs index 3f44d4f31b..341bb3e1cd 100644 --- a/tools/ea/Ea/Genesis.hs +++ b/tools/ea/Ea/Genesis.hs @@ -51,7 +51,11 @@ import Control.Lens import Data.Text import Chainweb.Graph +import Chainweb.Test.TestVersions import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet -- ---------------------------------------------------------------------- -- @@ -213,7 +217,7 @@ devnetKadOps = "pact/genesis/devnet/kad-ops-grants.yaml" fastTimedCPM0 :: Genesis fastTimedCPM0 = Genesis - { _version = FastTimedCPM petersonChainGraph + { _version = fastForkingCpmTestVersion petersonChainGraph , _tag = "FastTimedCPM" , _txChainId = Zero , _coinbase = Just fast0Grants diff --git a/tools/genconf/GenConf.hs b/tools/genconf/GenConf.hs index dc40bb88a9..88b7e5c193 100644 --- a/tools/genconf/GenConf.hs +++ b/tools/genconf/GenConf.hs @@ -23,6 +23,7 @@ import Chainweb.Chainweb.Configuration import Chainweb.HostAddress import Chainweb.Miner.Config import Chainweb.Version +import Chainweb.Version.Mainnet import P2P.Node.Configuration import P2P.Peer diff --git a/tools/header-dump/HeaderDump.hs b/tools/header-dump/HeaderDump.hs index 3d42ed2cd8..56adfa2704 100644 --- a/tools/header-dump/HeaderDump.hs +++ b/tools/header-dump/HeaderDump.hs @@ -106,6 +106,8 @@ import Chainweb.Time import Chainweb.TreeDB hiding (key) import Chainweb.Utils hiding (progress) import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Registry import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB @@ -220,7 +222,7 @@ instance ToJSON Config where toJSON o = object [ "logHandle" .= _configLogHandle o , "logLevel" .= _configLogLevel o - , "chainwebVersion" .= _configChainwebVersion o + , "chainwebVersion" .= _versionName (_configChainwebVersion o) , "chainId" .= _configChainId o , "pretty" .= _configPretty o , "database" .= _configDatabasePath o @@ -234,7 +236,8 @@ instance FromJSON (Config -> Config) where parseJSON = withObject "Config" $ \o -> id <$< configLogHandle ..: "logHandle" % o <*< configLogLevel ..: "logLevel" % o - <*< configChainwebVersion ..: "ChainwebVersion" % o + <*< setProperty configChainwebVersion "chainwebVersion" + (findKnownVersion <=< parseJSON) o <*< configChainId ..: "chainId" % o <*< configPretty ..: "pretty" % o <*< configDatabasePath ..: "database" % o @@ -247,7 +250,7 @@ pConfig :: MParser Config pConfig = id <$< configLogHandle .:: Y.pLoggerHandleConfig <*< configLogLevel .:: Y.pLogLevel - <*< configChainwebVersion .:: option textReader + <*< configChainwebVersion .:: option (findKnownVersion =<< textReader) % long "chainweb-version" <> help "chainweb version identifier" <*< configChainId .:: fmap Just % option textReader @@ -341,7 +344,7 @@ instance ToJSON a => ToJSON (ChainData a) where mainWithConfig :: Config -> IO () mainWithConfig config = withLog $ \logger -> liftIO $ run config $ logger - & addLabel ("version", toText $ _configChainwebVersion config) + & addLabel ("version", getChainwebVersionName $ _versionName $ _configChainwebVersion config) -- & addLabel ("chain", toText $ _configChainId config) where logconfig = Y.defaultLogConfig diff --git a/tools/run-nodes/RunNodes.hs b/tools/run-nodes/RunNodes.hs index 85a45d823e..fbaab75cb4 100644 --- a/tools/run-nodes/RunNodes.hs +++ b/tools/run-nodes/RunNodes.hs @@ -6,6 +6,7 @@ module RunNodes ( main, runNodesOpts ) where import Chainweb.Graph (petersonChainGraph) import Chainweb.Version +import Chainweb.Version.Registry import Control.Concurrent import Control.Concurrent.Async @@ -22,6 +23,7 @@ import System.Process (callProcess) -- internal modules +import Chainweb.Test.TestVersions import Chainweb.Utils --- @@ -48,14 +50,10 @@ pNodes = option auto <> help "Number of Nodes to run (default: 10)") pVersion :: Parser ChainwebVersion -pVersion = option cver +pVersion = option (findKnownVersion =<< textReader) (long "chainweb-version" <> metavar "CHAINWEB_VERSION" - <> value (TimedCPM petersonChainGraph) + <> value (fastForkingCpmTestVersion petersonChainGraph) <> help "Chainweb Version to run the Nodes with (default: timedCPM-peterson)") - where - cver :: ReadM ChainwebVersion - cver = eitherReader $ \s -> - note "Illegal ChainwebVersion" . chainwebVersionFromText $ T.pack s pConfig :: Parser FilePath pConfig = strOption @@ -73,7 +71,7 @@ runNode nid mconf (Env e ns v _ ps) = callProcess e (T.unpack <$> ops) ops = [ "--hostname=127.0.0.1" , "--node-id=" <> sshow nid , "--test-miners=" <> sshow ns - , "--chainweb-version=" <> chainwebVersionToText v + , "--chainweb-version=" <> getChainwebVersionName (_versionName v) , "--interface=127.0.0.1" ] <> maybe [] (\c -> ["--config-file=" <> T.pack c]) mconf diff --git a/tools/txstream/TxStream.hs b/tools/txstream/TxStream.hs index 8097eea0c6..ba40ad98df 100644 --- a/tools/txstream/TxStream.hs +++ b/tools/txstream/TxStream.hs @@ -73,6 +73,8 @@ import Chainweb.TreeDB import Chainweb.TreeDB.RemoteDB import Chainweb.Utils import Chainweb.Version +import Chainweb.Version.Development +import Chainweb.Version.Registry import Chainweb.Version.Utils import Data.LogMessage @@ -106,19 +108,17 @@ defaultConfig = Config { _configLogHandle = Y.StdOut , _configLogLevel = Y.Info , _configChainwebVersion = Development - , _configChainId = someChainId devVersion + , _configChainId = someChainId Development , _configNode = HostAddress (unsafeHostnameFromText "us1.tn1.chainweb.com") 443 , _configPretty = True , _configOutputs = True } - where - devVersion = Development instance ToJSON Config where toJSON o = object [ "logHandle" .= _configLogHandle o , "logLevel" .= _configLogLevel o - , "chainwebVersion" .= _configChainwebVersion o + , "chainwebVersion" .= _versionName (_configChainwebVersion o) , "chainId" .= _configChainId o , "node" .= _configNode o , "pretty" .= _configPretty o @@ -126,20 +126,22 @@ instance ToJSON Config where ] instance FromJSON (Config -> Config) where - parseJSON = withObject "Config" $ \o -> id - <$< configLogHandle ..: "logHandle" % o - <*< configLogLevel ..: "logLevel" % o - <*< configChainwebVersion ..: "ChainwebVersion" % o - <*< configChainId ..: "chainId" % o - <*< configNode ..: "node" % o - <*< configPretty ..: "pretty" % o - <*< configOutputs ..: "outputs" % o + parseJSON = withObject "Config" $ \o -> do + id + <$< configLogHandle ..: "logHandle" % o + <*< configLogLevel ..: "logLevel" % o + <*< setProperty configChainwebVersion "chainwebVersion" + (findKnownVersion <=< parseJSON) o + <*< configChainId ..: "chainId" % o + <*< configNode ..: "node" % o + <*< configPretty ..: "pretty" % o + <*< configOutputs ..: "outputs" % o pConfig :: MParser Config pConfig = id <$< configLogHandle .:: Y.pLoggerHandleConfig <*< configLogLevel .:: Y.pLogLevel - <*< configChainwebVersion .:: option textReader + <*< configChainwebVersion .:: option (findKnownVersion =<< textReader) % long "chainweb-version" <> help "chainweb version identifier" <*< configChainId .:: option textReader @@ -364,7 +366,7 @@ mainWithConfig config = withLog $ \logger -> do let logg :: LogFunction logg = logFunction $ logger & addLabel ("host", toText $ _configNode config) - & addLabel ("version", toText $ _configChainwebVersion config) + & addLabel ("version", toText $ _versionName $ _configChainwebVersion config) & addLabel ("chain", toText $ _configChainId config) liftIO $ if _configOutputs config then runOutputs config logg From b4b9d3cac14ab8de4dd2fd0f783439e5fab79057 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 17:11:11 -0500 Subject: [PATCH 02/91] add test registry --- test/Chainweb/Test/TestVersions.hs | 89 +++++++++++++++--------------- 1 file changed, 46 insertions(+), 43 deletions(-) diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index bc68e4e5f1..4d17bfd7c7 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -2,6 +2,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -17,9 +18,11 @@ module Chainweb.Test.TestVersions import Control.Lens hiding (elements) import Data.Bits import Data.Foldable +import Data.Function import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS +import qualified Data.List as List import Data.Word import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload as TN0 import qualified Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload as TNN @@ -66,37 +69,39 @@ testBootstrapPeerInfos = } } -data GraphPos = P1 | P2 deriving (Bounded, Enum) - -graphToCodeN :: GraphPos -> KnownGraph -> Word32 -graphToCodeN p g = shiftL (graphToCode g) (4 * (4 + fromEnum p)) - where - graphToCode :: KnownGraph -> Word32 - graphToCode Singleton = 0x00000001 - graphToCode Pair = 0x00000002 - graphToCode Triangle = 0x00000003 - graphToCode Peterson = 0x00000004 - graphToCode Twenty = 0x00000005 - graphToCode HoffmanSingleton = 0x00000006 - legalizeTestVersion :: (ChainwebVersion -> ChainwebVersion) -> ChainwebVersion legalizeTestVersion f = unsafePerformIO $ do - let v = f v + let v = f v registerVersion v return v --- edtodo: test registry list for codes -testRegistry :: [ChainwebVersion] +-- edtodo: document +-- all chainweb versions used in tests *must* be included in this list to be assigned +-- a version code, and also registered via legalizeTestVersion into the version registry. +-- failure to do so will result in runtime errors. +testRegistry :: [ChainwebVersionName] testRegistry = concat - [ [ fastForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ slowForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ barebonesTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ noBridgeCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ timedConsensusVersion (knownChainGraph g1) (knownChainGraph g2) | g1 :: KnownGraph <- [minBound..maxBound], g2 :: KnownGraph <- [minBound..maxBound] ] + [ [ _versionName $ fastForkingCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] + ] + , [ _versionName $ slowForkingCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] + ] + , [ _versionName $ barebonesTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] + ] + , [ _versionName $ noBridgeCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] + ] + , [ _versionName $ timedConsensusVersion' (knownChainGraph g1) (knownChainGraph g2) undefined + | g1 :: KnownGraph <- [minBound..maxBound] + , g2 :: KnownGraph <- [minBound..maxBound] + ] ] testVersionTemplate :: ChainwebVersion -> ChainwebVersion testVersionTemplate v = v + & versionCode .~ ChainwebVersionCode (int $ fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) & versionHeaderBaseSizeBytes .~ 318 - 110 & versionWindow .~ Nothing & versionFakeFirstEpochStart .~ False -- DA is already disabled with window = Nothing @@ -129,11 +134,11 @@ fastForks = HM.fromList , (Chainweb217Pact, AllChains (BlockHeight 20)) ] -barebonesTestVersion :: ChainGraph -> ChainwebVersion -barebonesTestVersion g = legalizeTestVersion $ \v -> +barebonesTestVersion :: ChainGraph -> ChainwebVersion +barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) + +barebonesTestVersion' g v = testVersionTemplate v - & versionCode .~ ChainwebVersionCode - (0x80000000 .|. graphToCodeN P1 (view chainGraphKnown g)) & versionWindow .~ Nothing & versionBlockRate .~ BlockRate 0 & versionName .~ ChainwebVersionName ("test-" <> toText g) @@ -185,11 +190,11 @@ cpmTestVersion g v = v (onChains [(unsafeChainId 3, HM.singleton (BlockHeight 2) (Upgrade MNKAD.transactions False))]) slowForkingCpmTestVersion :: ChainGraph -> ChainwebVersion -slowForkingCpmTestVersion g = legalizeTestVersion $ \v -> +slowForkingCpmTestVersion g = legalizeTestVersion (slowForkingCpmTestVersion' g) + +slowForkingCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) - & versionName .~ ChainwebVersionName ("slowfork-CPM-test-" <> toText g) - & versionCode .~ ChainwebVersionCode - (0x80000003 .|. graphToCodeN P1 (view chainGraphKnown g)) + & versionName .~ ChainwebVersionName ("slowfork-CPM-" <> toText g) & versionForks .~ HM.fromList [ (SlowEpoch, AllChains (BlockHeight 0)) , (OldTargetGuard, AllChains (BlockHeight 0)) @@ -216,30 +221,27 @@ slowForkingCpmTestVersion g = legalizeTestVersion $ \v -> ] fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion -fastForkingCpmTestVersion g = legalizeTestVersion $ \v -> +fastForkingCpmTestVersion g = legalizeTestVersion (fastForkingCpmTestVersion' g) + +fastForkingCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("fastfork-CPM-" <> toText g) - & versionCode .~ ChainwebVersionCode - (0x80000004 .|. graphToCodeN P1 (view chainGraphKnown g)) & versionForks .~ fastForks noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion -noBridgeCpmTestVersion g = legalizeTestVersion $ \v -> +noBridgeCpmTestVersion g = legalizeTestVersion (noBridgeCpmTestVersion' g) + +noBridgeCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) - & versionCode .~ ChainwebVersionCode - (0x80000005 .|. graphToCodeN P1 (view chainGraphKnown g)) & versionForks .~ (fastForks & at SPVBridge .~ Just (AllChains maxBound)) -timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion -timedConsensusVersion g1 g2 = legalizeTestVersion $ \v -> +timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion +timedConsensusVersion g1 g2 = legalizeTestVersion (timedConsensusVersion' g1 g2) + +timedConsensusVersion' g1 g2 v = testVersionTemplate v & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) - & versionCode .~ ChainwebVersionCode - (foldl' (.|.) 0x80000001 - [ graphToCodeN P1 (view chainGraphKnown g1) - , graphToCodeN P2 (view chainGraphKnown g2) - ]) & versionBlockRate .~ BlockRate 1_000_000 & versionWindow .~ Nothing & versionForks .~ HM.fromList @@ -266,4 +268,5 @@ timedConsensusVersion g1 g2 = legalizeTestVersion $ \v -> [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] , _genesisBlockTarget = AllChains maxTarget , _genesisTime = AllChains $ BlockCreationTime epoch - } \ No newline at end of file + } + From 6bc4aacd71ed6d5808f3572c2783602611a80ace Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 20:08:12 -0500 Subject: [PATCH 03/91] re-run ea and fix tests --- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 8 ++--- test/Chainweb/Test/Rosetta/RestAPI.hs | 2 +- test/Chainweb/Test/TestVersions.hs | 34 +++++++++---------- test/golden/empty-block-tests-expected.txt | 6 ++-- test/golden/new-block-0-expected.txt | 8 ++--- tools/cwtool/TxSimulator.hs | 1 + tools/ea/Ea.hs | 5 +-- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 6e5fb66427..8600b472d2 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -244,7 +244,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(enforce false 'hi)") $ assertTxFailure "Should fail with the error from the enforce" "hi" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce pre-fork evaluates the string with gas" 35 + assertTxGas "Enforce pre-fork evaluates the string with gas" 34 , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions pre-fork gas" 20 , PactTxTest @@ -258,7 +258,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(+ 1 \'clearlyanerror)") $ assertTxFailure "Should replace tx error with empty error" "" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce post fork does not eval the string" (15 + coinTxBuyTransferGas) + assertTxGas "Enforce post fork does not eval the string" (14 + coinTxBuyTransferGas) , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions post-fork change gas" (40 + coinTxBuyTransferGas) , PactTxTest @@ -655,7 +655,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Pre-fork format gas" 21 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Pre-fork try" 19 + assertTxGas "Pre-fork try" 18 , PactTxTest (buildSimpleCmd defineNonNamespacedPreFork) $ assertTxSuccess "Should pass when defining a non-namespaced keyset" @@ -671,7 +671,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Post-fork format gas increase" 48 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Post-fork try should charge a bit more gas" 20 + assertTxGas "Post-fork try should charge a bit more gas" 19 , PactTxTest (buildSimpleCmd defineNonNamespacedPostFork1) $ assertTxFailure "Should fail when defining a non-namespaced keyset post fork" diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 5042153f6a..024e608317 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -727,7 +727,7 @@ nid = NetworkId } genesisId :: BlockId -genesisId = BlockId 0 "gl2bDgfL9ZRJCe0VkGZq8pfCl1PazbfYsSAZNHp8giI" +genesisId = BlockId 0 "dqdUQNqEXcdMDeb6xWXuv1_KvLvDXysgsaEU8ZfLs9Q" rosettaVersion :: RosettaNodeVersion rosettaVersion = RosettaNodeVersion diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 4d17bfd7c7..53709146eb 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -71,37 +71,37 @@ testBootstrapPeerInfos = legalizeTestVersion :: (ChainwebVersion -> ChainwebVersion) -> ChainwebVersion legalizeTestVersion f = unsafePerformIO $ do - let v = f v + let v = f v registerVersion v return v -- edtodo: document --- all chainweb versions used in tests *must* be included in this list to be assigned +-- all chainweb versions used in tests *must* be included in this list to be assigned -- a version code, and also registered via legalizeTestVersion into the version registry. -- failure to do so will result in runtime errors. testRegistry :: [ChainwebVersionName] testRegistry = concat - [ [ _versionName $ fastForkingCpmTestVersion' (knownChainGraph g) undefined - | g :: KnownGraph <- [minBound..maxBound] + [ [ _versionName $ fastForkingCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ slowForkingCpmTestVersion' (knownChainGraph g) undefined - | g :: KnownGraph <- [minBound..maxBound] + , [ _versionName $ slowForkingCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ barebonesTestVersion' (knownChainGraph g) undefined - | g :: KnownGraph <- [minBound..maxBound] + , [ _versionName $ barebonesTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ noBridgeCpmTestVersion' (knownChainGraph g) undefined - | g :: KnownGraph <- [minBound..maxBound] + , [ _versionName $ noBridgeCpmTestVersion' (knownChainGraph g) undefined + | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ timedConsensusVersion' (knownChainGraph g1) (knownChainGraph g2) undefined + , [ _versionName $ timedConsensusVersion' (knownChainGraph g1) (knownChainGraph g2) undefined | g1 :: KnownGraph <- [minBound..maxBound] - , g2 :: KnownGraph <- [minBound..maxBound] + , g2 :: KnownGraph <- [minBound..maxBound] ] ] testVersionTemplate :: ChainwebVersion -> ChainwebVersion testVersionTemplate v = v - & versionCode .~ ChainwebVersionCode (int $ fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + 0x80000000) & versionHeaderBaseSizeBytes .~ 318 - 110 & versionWindow .~ Nothing & versionFakeFirstEpochStart .~ False -- DA is already disabled with window = Nothing @@ -134,7 +134,7 @@ fastForks = HM.fromList , (Chainweb217Pact, AllChains (BlockHeight 20)) ] -barebonesTestVersion :: ChainGraph -> ChainwebVersion +barebonesTestVersion :: ChainGraph -> ChainwebVersion barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) barebonesTestVersion' g v = @@ -231,15 +231,15 @@ fastForkingCpmTestVersion' g v = noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion noBridgeCpmTestVersion g = legalizeTestVersion (noBridgeCpmTestVersion' g) -noBridgeCpmTestVersion' g v = +noBridgeCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) & versionForks .~ (fastForks & at SPVBridge .~ Just (AllChains maxBound)) -timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion +timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion timedConsensusVersion g1 g2 = legalizeTestVersion (timedConsensusVersion' g1 g2) -timedConsensusVersion' g1 g2 v = +timedConsensusVersion' g1 g2 v = testVersionTemplate v & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) & versionBlockRate .~ BlockRate 1_000_000 diff --git a/test/golden/empty-block-tests-expected.txt b/test/golden/empty-block-tests-expected.txt index fa4e2d9671..35fcbc4b6d 100644 --- a/test/golden/empty-block-tests-expected.txt +++ b/test/golden/empty-block-tests-expected.txt @@ -3,6 +3,6 @@ results: transactions: [] minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119 transactionsHash: lL9ztEiU-NwzrlTpBbvhT4M1l5Shsht94OwFyhBaFD0 - outputsHash: fb3bO384oIyom069n0hV7P8L-Hnrk5tKvKFW-yHMMT0 - payloadHash: TB6Wg1ztCYu86QOVESUYe_S45HCDRHqBFznzmwpEx9c - coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa041UkVSck1YRTRTVjh3VkZoTVlVNXFWRTVTV1dWTWJYVm9WbHBNY1RSZk9XOVNPR0pCYVd0WFkwa2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 + outputsHash: VRjt10NtZ4DeDlMkZ7icInAhi9FWeqxGZ2-YEbOWi1g + payloadHash: yyp93aOQX012ivpWDPlX2PJOZ4H8775rB09gNvygcKE + coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa1ZUUzJ0Tk9YSXphbXBUYlZBemJrbzJRMEpmWkU5SGFVaDFUMkprVFV0dFEwUm5URUZuV2t4YVIzY2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 diff --git a/test/golden/new-block-0-expected.txt b/test/golden/new-block-0-expected.txt index 8645cdd1b8..3dead33e91 100644 --- a/test/golden/new-block-0-expected.txt +++ b/test/golden/new-block-0-expected.txt @@ -10,7 +10,7 @@ results: - - eyJoYXNoIjoicFBrLWxFUE1jRnRDWXdXR2ZaejV2bGdzZ1ZYV3dYbXhmQVJ6bkFCbUVONCIsInNpZ3MiOlt7InNpZyI6IjM3YjI4MmY1YWQ0YWY0Yzk2YmE1NzA5YWEzOTI2NTU1YjU5MDEwYzczZmVmZTUwNzQyMGYyODVjZTI1MmQ5ZjM1YzQ5NjAxYTgwMDYxZGU2NzEzNTcxNzY2MTNmODIyODQxMDU0MjliZTVkOTEzNWMzODAxMzcyN2UyMmI3ZjA2In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihmcmVlLnRlc3QxLnRyYW5zZmVyIFxcXCJBY2N0MVxcXCIgXFxcIkFjY3QyXFxcIiAxLjAwKVwifX0sXCJzaWduZXJzXCI6W3tcInB1YktleVwiOlwiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwifV0sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjEwMDAwMDAsXCJnYXNMaW1pdFwiOjEwMDAwLFwiY2hhaW5JZFwiOlwiMFwiLFwiZ2FzUHJpY2VcIjoxLjBlLTIsXCJzZW5kZXJcIjpcInNlbmRlcjAwXCJ9LFwibm9uY2VcIjpcIjEzXCJ9In0 - eyJnYXMiOjQwNywicmVzdWx0Ijp7InN0YXR1cyI6InN1Y2Nlc3MiLCJkYXRhIjoiV3JpdGUgc3VjY2VlZGVkIn0sInJlcUtleSI6InBQay1sRVBNY0Z0Q1l3V0dmWno1dmxnc2dWWFd3WG14ZkFSem5BQm1FTjQiLCJsb2dzIjoib3hWd2tvU21xM2JZeVh5aHYwT0V3ZTN2c0tGbWlkRTBtTEotQ2FZWVhRayIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjIwfQ - - eyJoYXNoIjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsInNpZ3MiOlt7InNpZyI6IjZhODRiMWRkNGFkNDE5ZGY3YThhODA4NGYwNWEyZDM5YzExNTBmMjI5ZTM3OTFlM2UwY2E0OTkyZDkwNGNhOGVlMTk4MTljNTE3YTVjNjM5M2E3ZmYyNWI1NjdiNjJjOTYwZTFiYmI4ZTE2YjZiMWU2YmRkNDU1Njk3ODc1MDA5In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAncHJldi1ibG9jay1oYXNoIChjaGFpbi1kYXRhKSlcIn19LFwic2lnbmVyc1wiOlt7XCJwdWJLZXlcIjpcIjM2ODgyMGY4MGMzMjRiYmM3YzJiMDYxMDY4OGE3ZGE0M2UzOWY5MWQxMTg3MzI2NzFjZDljNzUwMGZmNDNjY2FcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxMDAwMDAwLFwiZ2FzTGltaXRcIjoxMDAwMCxcImNoYWluSWRcIjpcIjBcIixcImdhc1ByaWNlXCI6MS4wZS0yLFwic2VuZGVyXCI6XCJzZW5kZXIwMFwifSxcIm5vbmNlXCI6XCIxNFwifSJ9 - - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6ImZyS2FhZ2dyRHdKeE1CTWlPVDgyblZnNzRueXdWYlNrM1BfT0tCTVNtV28ifSwicmVxS2V5IjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsImxvZ3MiOiJUVVVlWFJmRFlnQlZTdjFZU0ktU3lfRVdtcHRaWXZqQlNmY1ZMc2ZzcVpVIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MjN9 + - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkhJT1hjdHJHUmpaMTdzWHo0WEpoTkNwa1FMNXFzU2dQX3NZejM1c2d2blUifSwicmVxS2V5IjoiX2RneUxfSGtlZGJFZjl5UTY5Wkhhc2g2M01JVUw2ZktibzBqeXJJRWhPYyIsImxvZ3MiOiJUVVVlWFJmRFlnQlZTdjFZU0ktU3lfRVdtcHRaWXZqQlNmY1ZMc2ZzcVpVIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MjN9 - - eyJoYXNoIjoiTzhoWU1TVUhnSkNPRXFoTy0yRFRNRGJhTzBIZmx6VjM0UHRzM0dlUHBJUSIsInNpZ3MiOlt7InNpZyI6ImMxYjQ5OTQwNTQ0MjZmNzE2NWM2NmZjNjY0MzE0N2M1N2QxYTczYzM3NjEwMjU4OGU0M2QyMzA1ZjBhY2FhYTQyMDRkZGRmNGRhMWFiMzk0OTdkY2FiM2FjZWU1NmQ0NmQ3NzJkNDZkNjAyNmU5ZjQ4MGFmMWNiMWIyNWFmYzA3In1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpXCJ9fSxcInNpZ25lcnNcIjpbe1wicHViS2V5XCI6XCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJ9XSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTAwMDAwMCxcImdhc0xpbWl0XCI6MTAwMDAsXCJjaGFpbklkXCI6XCIwXCIsXCJnYXNQcmljZVwiOjEuMGUtMixcInNlbmRlclwiOlwic2VuZGVyMDBcIn0sXCJub25jZVwiOlwiMTVcIn0ifQ - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6eyJ0aW1lIjoiMTk3MC0wMS0wMVQwMDowMDowMFoifX0sInJlcUtleSI6Ik84aFlNU1VIZ0pDT0VxaE8tMkRUTURiYU8wSGZselYzNFB0czNHZVBwSVEiLCJsb2dzIjoidTdKNDU3dGdzNDJ1dGhjOU1TbWtkaTkxT2tUb2FCNFpGei1QUWtSajJhdyIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjI2fQ - - eyJoYXNoIjoiSGFsbTJLYUV5Qm5weXJqMkxHMmJQcVZDUmZBYk8yWHZzZWlnaURaZElLOCIsInNpZ3MiOlt7InNpZyI6ImI0MDNlZWI3ODI0NzA0OWU0NjEzNGQzYWQ0MWZlMTAwNWMxM2I0NzNhNjUzZGNmNTVlN2ZmMzIzOGU5N2VkYmRmZTMyZmFmODczODViYTc0MmNmMDFmYmFkMDU2ODEwNWVjNDhhMDk1MzIyNWJmMmY0ODJjNjhiMjkyZGNmOTAzIn1dLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1widGVzdC1hZG1pbi1rZXlzZXRcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdfSxcImNvZGVcIjpcIihhdCAnYmxvY2staGVpZ2h0IChjaGFpbi1kYXRhKSlcIn19LFwic2lnbmVyc1wiOlt7XCJwdWJLZXlcIjpcIjM2ODgyMGY4MGMzMjRiYmM3YzJiMDYxMDY4OGE3ZGE0M2UzOWY5MWQxMTg3MzI2NzFjZDljNzUwMGZmNDNjY2FcIn1dLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxMDAwMDAwLFwiZ2FzTGltaXRcIjoxMDAwMCxcImNoYWluSWRcIjpcIjBcIixcImdhc1ByaWNlXCI6MS4wZS0yLFwic2VuZGVyXCI6XCJzZW5kZXIwMFwifSxcIm5vbmNlXCI6XCIxNlwifSJ9 @@ -25,6 +25,6 @@ results: - eyJnYXMiOjcsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6InNlbmRlcjAwIn0sInJlcUtleSI6IldnbnVDZzZMX2w2bHpiald0QmZNRXVQdHR5X3VHY05yVW9sNUhHUkVPX28iLCJsb2dzIjoiVl96OG10RjdtMVpNRWJiQUNtZlFLbWFLejdhZF9qWjhYbXhaYV9KMTRWWSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjQxfQ minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119 transactionsHash: jI_aVcPfGdURmGMHOnzx9YdhzAQBvRZVLKQdpmd_9BQ - outputsHash: O2u6JQsu3zygT_2GH5KHWF7izbiD_ZWdk9rO0SdkMWQ - payloadHash: kfMV0MFKedYBe40R9_0EUMrSRDcQ9dtsooAjA2K1KWE - coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa041UkVSck1YRTRTVjh3VkZoTVlVNXFWRTVTV1dWTWJYVm9WbHBNY1RSZk9XOVNPR0pCYVd0WFkwa2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 + outputsHash: mAsyu-5vlSqZvInKbE24P97X9sl0OYIK9l6n70eF4mk + payloadHash: DwseQFWwG2dgAyoxbDk8j44ILsNILhK7aKNc-J9CDIo + coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJJa1ZUUzJ0Tk9YSXphbXBUYlZBemJrbzJRMEpmWkU5SGFVaDFUMkprVFV0dFEwUm5URUZuV2t4YVIzY2kiLCJsb2dzIjoibFdaVWZjX0dKdGxUUy1LeDAtYm00aXRfSWE5bnNGZG5jZ2NwSE0td091OCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjd9 diff --git a/tools/cwtool/TxSimulator.hs b/tools/cwtool/TxSimulator.hs index ba57067aa0..1c00070c75 100644 --- a/tools/cwtool/TxSimulator.hs +++ b/tools/cwtool/TxSimulator.hs @@ -49,6 +49,7 @@ import Chainweb.Transaction import Chainweb.Utils import Chainweb.Utils.Paging import Chainweb.Version +import Chainweb.Version.Guards import Chainweb.Version.Mainnet import Chainweb.Version.Registry diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index 784dfbdfd6..37fe0370e4 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -295,15 +295,16 @@ startTxModule tag = , "module Chainweb.Pact.Transactions." <> tag <> "Transactions ( transactions ) where" , "" , "import Data.Bifunctor (first)" + , "import System.IO.Unsafe" , "" , "import Chainweb.Transaction" , "import Chainweb.Utils" , "" - , "transactions :: IO [ChainwebTransaction]" + , "transactions :: [ChainwebTransaction]" , "transactions =" , " let decodeTx t =" , " fromEitherM . (first (userError . show)) . codecDecode (chainwebPayloadCodec maxBound) =<< decodeB64UrlNoPaddingText t" - , " in mapM decodeTx [" + , " in unsafePerformIO $ mapM decodeTx [" ] endTxModule :: [Text] From 75040d2fb633b753ca88152a2d05d068a8b89737 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 22:37:53 -0500 Subject: [PATCH 04/91] clear up some todos --- node/ChainwebNode.hs | 2 + src/Chainweb/BlockHeader.hs | 3 +- src/Chainweb/ChainId.hs | 1 + src/Chainweb/Pact/TransactionExec.hs | 2 - src/Chainweb/Utils.hs | 6 +++ src/Chainweb/Version.hs | 17 +++--- src/Chainweb/Version/Codes.hs | 3 -- src/Chainweb/Version/Development.hs | 64 +++++++++++++--------- src/Chainweb/Version/Mainnet.hs | 79 +++++++++++++++------------- src/Chainweb/Version/Registry.hs | 53 +++++++++++++------ src/Chainweb/Version/Testnet.hs | 58 ++++++++++---------- test/Chainweb/Test/TestVersions.hs | 5 +- test/ChainwebTests.hs | 21 ++++---- tools/cwtool/CwTool.hs | 4 ++ 14 files changed, 183 insertions(+), 135 deletions(-) delete mode 100644 src/Chainweb/Version/Codes.hs diff --git a/node/ChainwebNode.hs b/node/ChainwebNode.hs index f8a1768d2b..d7d254b1bd 100644 --- a/node/ChainwebNode.hs +++ b/node/ChainwebNode.hs @@ -99,6 +99,7 @@ import Chainweb.Utils import Chainweb.Utils.RequestLog import Chainweb.Version import Chainweb.Version.Mainnet +import Chainweb.Version.Registry import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB @@ -528,6 +529,7 @@ main = do checkRLimits runWithPkgInfoConfiguration mainInfo pkgInfo $ \conf -> do let v = _configChainwebVersion $ _nodeConfigChainweb conf + registerVersion v hSetBuffering stderr LineBuffering withNodeLogger (_nodeConfigLog conf) v $ \logger -> do logFunctionJson logger Info ProcessStarted diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index a71b07407f..a794a78146 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -669,8 +669,7 @@ data BlockHeader :: Type where , _blockParent :: {-# UNPACK #-} !BlockHash -- ^ authoritative - , _blockAdjacentHashes :: BlockHashRecord - -- edtodo: document why this is lazy + , _blockAdjacentHashes :: !BlockHashRecord -- ^ authoritative , _blockTarget :: {-# UNPACK #-} !HashTarget diff --git a/src/Chainweb/ChainId.hs b/src/Chainweb/ChainId.hs index e313d62d80..2933f47a6b 100644 --- a/src/Chainweb/ChainId.hs +++ b/src/Chainweb/ChainId.hs @@ -57,6 +57,7 @@ module Chainweb.ChainId , unsafeChainId , chainIdInt +-- * Mapping from chain IDs to values , ChainMap(..) , onChain , onChains diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index eb25560854..905de16fbf 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -733,11 +733,9 @@ applyExec' initialGas interp (ExecMsg parsedCode execData) senderSigs hsh nsp return er --- edtodo use cid enablePactEvents' :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePactEvents' v cid bh = [FlagDisablePactEvents | not (enablePactEvents v cid bh)] --- edtodo use cid enforceKeysetFormats' :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enforceKeysetFormats' v cid bh = [FlagEnforceKeyFormats | enforceKeysetFormats v cid bh] diff --git a/src/Chainweb/Utils.hs b/src/Chainweb/Utils.hs index 28d3236b35..14cb69edbf 100644 --- a/src/Chainweb/Utils.hs +++ b/src/Chainweb/Utils.hs @@ -66,6 +66,7 @@ module Chainweb.Utils , len , (==>) , keySet +, tabulateHashMap , minimumsOf , minimumsByOf , maxBy @@ -359,6 +360,11 @@ keySet :: HM.HashMap a b -> HS.HashSet a keySet = HS.fromMap . set each () {-# INLINE keySet #-} +-- | A HashMap memoizing a function over a finite input type. +-- +tabulateHashMap :: (Enum a, Bounded a, Hashable a, Eq a) => (a -> b) -> HM.HashMap a b +tabulateHashMap f = HM.fromList [ (a, f a) | a <- [minBound..maxBound] ] + -- | The the minimum elements of a list. -- minimumsOf :: Ord a => Getting (Endo (Endo [a])) s a -> s -> [a] diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 53b33f9150..20b8515d44 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -282,8 +282,10 @@ newtype ChainwebVersionCode = data Upgrade = Upgrade { _upgradeTransactions :: [ChainwebTransaction] - -- edtodo document , _legacyUpgradeIsPrecocious :: Bool + -- ^ when set to `True`, the upgrade transactions are executed using the forks of the subsequent + -- block, rather than the block the upgrade transactions are included in. + -- do not use this for new upgrades unless you are sure you need it. } deriving stock (Generic) deriving anyclass (NFData) @@ -326,9 +328,8 @@ blockRate = _versionBlockRate instance Ord ChainwebVersion where v `compare` v' = fold - -- edtodo: doc - -- [ _versionCode v `compare` _versionCode v' - [ _versionName v `compare` _versionName v' + [ _versionCode v `compare` _versionCode v' + , _versionName v `compare` _versionName v' , _versionGraphs v `compare` _versionGraphs v' , _versionForks v `compare` _versionForks v' , _versionBlockRate v `compare` _versionBlockRate v' @@ -549,15 +550,12 @@ instance HasChainwebVersion ChainwebVersion where chainIds :: HasChainwebVersion v => v -> HS.HashSet ChainId chainIds = graphChainIds . snd . ruleHead . _versionGraphs . _chainwebVersion --- edtodo: doc +-- | Creates a map from fork heights to upgrades. forkUpgrades :: ChainwebVersion -> [(Fork, ChainMap Upgrade)] -> ChainMap (HashMap BlockHeight Upgrade) forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) - -- upgrades must not conflict - -- upgrades must be ordered like the forks are - -- upgrades must not be empty where conflictError fork h = error $ "conflicting upgrades at block height " <> show h <> " when adding upgrade for fork " <> show fork @@ -577,7 +575,8 @@ forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) , forkHeight /= maxBound ] --- edtodo: document +-- | The block height at all chains at which the latest known behavior changes +-- will have taken effect: forks, upgrade transactions, or graph changes. latestBehaviorAt :: ChainwebVersion -> BlockHeight latestBehaviorAt v = foldlOf' changes maxBlockHeight 0 v + 1 diff --git a/src/Chainweb/Version/Codes.hs b/src/Chainweb/Version/Codes.hs deleted file mode 100644 index a4b6fefd24..0000000000 --- a/src/Chainweb/Version/Codes.hs +++ /dev/null @@ -1,3 +0,0 @@ -module Chainweb.Version.Codes where - -mainnetCode = diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 540a8299ae..55109c83e6 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -1,8 +1,9 @@ +{-# language LambdaCase #-} {-# language NumericUnderscores #-} -{-# language PatternSynonyms #-} {-# language OverloadedStrings #-} -{-# language ViewPatterns #-} +{-# language PatternSynonyms #-} {-# language QuasiQuotes #-} +{-# language ViewPatterns #-} module Chainweb.Version.Development(devnet, pattern Development) where @@ -14,6 +15,7 @@ import Chainweb.ChainId import Chainweb.Difficulty import Chainweb.Graph import Chainweb.Time +import Chainweb.Utils import Chainweb.Utils.Rule import Chainweb.Version @@ -38,36 +40,46 @@ devnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000001 , _versionName = ChainwebVersionName "development" - , _versionForks = HM.unions - [ HM.fromList - [ (Chainweb217Pact, AllChains $ BlockHeight 20) - , (Chainweb216Pact, AllChains $ BlockHeight 19) - , (Chainweb215Pact, AllChains $ BlockHeight 18) - , (Chainweb214Pact, AllChains $ BlockHeight 17) - , (Chainweb213Pact, AllChains $ BlockHeight 16) - , (Pact420, AllChains $ BlockHeight 15) - , (Pact4Coin3, AllChains $ BlockHeight 14) - , (CoinV2, onChains $ concat + , _versionForks = tabulateHashMap $ \case + Vuln797Fix -> AllChains 1 + SlowEpoch -> AllChains 1 + OldTargetGuard -> AllChains 1 + EnforceKeysetFormats -> AllChains 1 + SkipFeatureFlagValidation -> AllChains 1 + OldDAGuard -> AllChains 1 + CheckTxHash -> AllChains 1 + PactEvents -> AllChains 1 + SkipTxTimingValidation -> AllChains 1 + SPVBridge -> AllChains 1 + ModuleNameFix -> AllChains 1 + ModuleNameFix2 -> AllChains 1 + PactBackCompat_v16 -> AllChains 1 + Pact44NewTrans -> AllChains 1 + Pact420 -> AllChains $ BlockHeight 1 + CoinV2 -> onChains $ concat [ [(unsafeChainId 0, BlockHeight 3)] , [(unsafeChainId i, BlockHeight 4) | i <- [1..19]] + ] + Pact4Coin3 -> AllChains $ BlockHeight 14 + Chainweb213Pact -> AllChains $ BlockHeight 15 + Chainweb214Pact -> AllChains $ BlockHeight 16 + Chainweb215Pact -> AllChains $ BlockHeight 17 + Chainweb216Pact -> AllChains $ BlockHeight 18 + Chainweb217Pact -> AllChains $ BlockHeight 18 + + , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) + [ forkUpgrades devnet + [ (CoinV2, onChains $ concat + [ [(unsafeChainId 0, upgrade Devnet.transactions)] + , [(unsafeChainId i, upgrade Devnet.transactions) | i <- [1..9]] ]) + , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) + , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) + , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) ] - -- all unspecified forks start at block 1 - , HM.fromList [(fork, AllChains 1) | fork <- [minBound..maxBound]] + , onChains [(unsafeChainId 0, HM.singleton to20ChainsDevelopment (upgrade MNKAD.transactions))] ] - , _versionUpgrades = chainZip HM.union - (forkUpgrades devnet - [ (CoinV2, onChains $ concat - [ [(unsafeChainId 0, upgrade Devnet.transactions)] - , [(unsafeChainId i, upgrade Devnet.transactions) | i <- [1..9]] - ]) - , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) - , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) - , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) - ]) - (onChains [(unsafeChainId 0, HM.singleton to20ChainsDevelopment (upgrade MNKAD.transactions))]) - , _versionGraphs = (to20ChainsDevelopment, twentyChainGraph) `Above` End petersonChainGraph diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 057ee0f670..4c58841976 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -1,8 +1,9 @@ +{-# language LambdaCase #-} {-# language NumericUnderscores #-} -{-# language PatternSynonyms #-} {-# language OverloadedStrings #-} -{-# language ViewPatterns #-} +{-# language PatternSynonyms #-} {-# language QuasiQuotes #-} +{-# language ViewPatterns #-} module Chainweb.Version.Mainnet(mainnet, pattern Mainnet01) where @@ -15,6 +16,7 @@ import Chainweb.ChainId import Chainweb.Difficulty import Chainweb.Graph import Chainweb.Time +import Chainweb.Utils import Chainweb.Utils.Rule import Chainweb.Version import P2P.BootstrapNodes @@ -90,31 +92,12 @@ mainnet :: ChainwebVersion mainnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000005 , _versionName = ChainwebVersionName "mainnet01" - , _versionForks = HM.fromList - [ (Chainweb217Pact, AllChains (BlockHeight 3_250_348)) -- 2022-12-02T00:00:00+00:00 - , (Chainweb216Pact, AllChains (BlockHeight 2_988_324)) -- 2022-09-02T00:00:00+00:00 - , (Chainweb215Pact, AllChains (BlockHeight 2_766_630)) -- 2022-06-17T00:00:00+00:00 - , (Chainweb214Pact, AllChains (BlockHeight 2_605_663)) -- 2022-04-22T00:00:00+00:00 - , (Chainweb213Pact, AllChains (BlockHeight 2_447_315)) -- 2022-02-26T00:00:00+00:00 - , (Pact420, AllChains (BlockHeight 2_334_500)) -- 2022-01-17T17:51:12+00:00 - , (Pact4Coin3, AllChains (BlockHeight 1_722_500)) -- 2021-06-19T03:34:05+00:00 - , (CoinV2, onChains $ - [ (unsafeChainId 0, BlockHeight 140_808) - , (unsafeChainId 1, BlockHeight 140_809) - , (unsafeChainId 2, BlockHeight 140_808) - , (unsafeChainId 3, BlockHeight 140_809) - , (unsafeChainId 4, BlockHeight 140_808) - , (unsafeChainId 5, BlockHeight 140_808) - , (unsafeChainId 6, BlockHeight 140_808) - , (unsafeChainId 7, BlockHeight 140_809) - , (unsafeChainId 8, BlockHeight 140_808) - , (unsafeChainId 9, BlockHeight 140_808) - ]) - , (SlowEpoch, AllChains 80_000) - , (OldTargetGuard, AllChains 452_820) -- ~ 2020-04-04T00:00:00Z - , (SkipFeatureFlagValidation, AllChains 530_500) -- ~ 2020-05-01T00:00:xxZ - , (OldDAGuard, AllChains 771_414) -- ~ 2020-07-23 16:00:00 - , (Vuln797Fix, onChains $ + , _versionForks = tabulateHashMap $ \case + SlowEpoch -> AllChains 80_000 + OldTargetGuard -> AllChains 452_820 -- ~ 2020-04-04T00:00:00Z + SkipFeatureFlagValidation -> AllChains 530_500 -- ~ 2020-05-01T00:00:xxZ + OldDAGuard -> AllChains 771_414 -- ~ 2020-07-23 16:00:00 + Vuln797Fix -> onChains $ [ (unsafeChainId 0, BlockHeight 121_452) -- 2019-12-10T21:00:00.0 , (unsafeChainId 1, BlockHeight 121_452) , (unsafeChainId 2, BlockHeight 121_452) @@ -125,17 +108,37 @@ mainnet = ChainwebVersion , (unsafeChainId 7, BlockHeight 121_451) , (unsafeChainId 8, BlockHeight 121_452) , (unsafeChainId 9, BlockHeight 121_451) - ] <> [(unsafeChainId i, BlockHeight 0) | i <- [10..19]]) - , (PactBackCompat_v16, AllChains 328_000) - , (SkipTxTimingValidation, AllChains 449_940) - , (ModuleNameFix, AllChains 448_501) - , (ModuleNameFix2, AllChains 752_214) - , (PactEvents, AllChains 1_138_000) - , (SPVBridge, AllChains 1_275_000) - , (EnforceKeysetFormats, AllChains 2_162_000) -- 2022-01-17T17:51:12 - , (CheckTxHash, AllChains 2_349_800) -- 2022-01-23T02:53:38 - , (Pact44NewTrans, AllChains 2_965_885) -- Todo: add date - ] + ] <> [(unsafeChainId i, BlockHeight 0) | i <- [10..19]] + PactBackCompat_v16 -> AllChains 328_000 + SkipTxTimingValidation -> AllChains 449_940 + ModuleNameFix -> AllChains 448_501 + ModuleNameFix2 -> AllChains 752_214 + PactEvents -> AllChains 1_138_000 + SPVBridge -> AllChains 1_275_000 + EnforceKeysetFormats -> AllChains 2_162_000 -- 2022-01-17T17:51:12 + CheckTxHash -> AllChains 2_349_800 -- 2022-01-23T02:53:38 + Pact44NewTrans -> AllChains 2_965_885 -- Todo: add date + Pact420 -> AllChains (BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 + + CoinV2 -> onChains + [ (unsafeChainId 0, BlockHeight 140_808) + , (unsafeChainId 1, BlockHeight 140_809) + , (unsafeChainId 2, BlockHeight 140_808) + , (unsafeChainId 3, BlockHeight 140_809) + , (unsafeChainId 4, BlockHeight 140_808) + , (unsafeChainId 5, BlockHeight 140_808) + , (unsafeChainId 6, BlockHeight 140_808) + , (unsafeChainId 7, BlockHeight 140_809) + , (unsafeChainId 8, BlockHeight 140_808) + , (unsafeChainId 9, BlockHeight 140_808) + ] + Pact4Coin3 -> AllChains (BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 + Chainweb213Pact -> AllChains (BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 + Chainweb214Pact -> AllChains (BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 + Chainweb215Pact -> AllChains (BlockHeight 2_766_630) -- 2022-06-17T00:00:00+00:00 + Chainweb216Pact -> AllChains (BlockHeight 2_988_324) -- 2022-09-02T00:00:00+00:00 + Chainweb217Pact -> AllChains (BlockHeight 3_250_348) -- 2022-12-02T00:00:00+00:00 + , _versionGraphs = (to20ChainsMainnet, twentyChainGraph) `Above` End petersonChainGraph diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index 0a019d7bcf..75121c50c2 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -1,8 +1,21 @@ {-# language RecordWildCards #-} +-- | +-- Module: Chainweb.Version.Registry +-- Copyright: Copyright © 2023 Kadena LLC. +-- License: MIT +-- Maintainer: Edmund Noble +-- Stability: experimental +-- +-- At certain points (in particular when decoding block headers) we need to be +-- able to look up ChainwebVersions by their version codes. We know of mainnet, +-- testnet, and devnet versions in prod code, but we don't know of testing +-- versions, and we also don't know if the user has enabled a flag that modifies +-- the devnet version, so we maintain a mutable registry mapping codes to +-- versions in this module. +-- module Chainweb.Version.Registry ( registerVersion - , validateVersion , lookupVersionByCode , knownVersions , findKnownVersion @@ -11,6 +24,7 @@ module Chainweb.Version.Registry import Control.DeepSeq import Control.Exception +import Control.Lens import Data.Foldable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM @@ -29,13 +43,14 @@ import Chainweb.Version.Testnet {-# NOINLINE versionMap #-} versionMap :: IORef (HashMap ChainwebVersionCode ChainwebVersion) versionMap = unsafePerformIO $ do - traverse_ validateVersion knownVersions - newIORef HM.empty + traverse_ (evaluate . rnf) knownVersions + newIORef $ HM.fromList [(_versionCode v, v) | v <- [mainnet, testnet]] --- disallow duplicates +-- | Register a version into our registry by code, ensuring it contains no +-- errors and there are no others registered with that code. registerVersion :: ChainwebVersion -> IO () registerVersion v = do - validateVersion v + evaluate (rnf v) atomicModifyIORef' versionMap $ \m -> case HM.lookup (_versionCode v) m of Just v' @@ -44,24 +59,30 @@ registerVersion v = do Nothing -> (HM.insert (_versionCode v) v m, ()) -validateVersion :: ChainwebVersion -> IO () -validateVersion v = do -- edtodo - evaluate (rnf v) - --- edtodo: doc +-- | Look up a version in the registry by code. lookupVersionByCode :: HasCallStack => ChainwebVersionCode -> ChainwebVersion lookupVersionByCode code - | Just v <- find (\v -> _versionCode v == code) knownVersions = v - | otherwise = lazify (unsafeDupablePerformIO $ do - m <- readIORef versionMap - return $ fromMaybe (error $ "version not registered with code " <> show code) $ HM.lookup code m - ) { _versionCode = code } + -- | these two cases exist to ensure that the mainnet and testnet versions + -- cannot be accidentally replaced and are the most performant to look up. + -- registering them is still allowed, as long as they are not conflicting. + | code == _versionCode mainnet = mainnet + | code == _versionCode testnet = testnet + | otherwise = + -- Setting the version code here allows us to delay doing the lookup in + -- the case that we don't actually need the version, just the code. + lookupVersion & versionCode .~ code where - lazify ~ChainwebVersion{..} = ChainwebVersion{..} + lookupVersion = unsafeDupablePerformIO $ do + m <- readIORef versionMap + return $ fromMaybe (error notRegistered) $ HM.lookup code m + notRegistered = "version not registered with code " <> show code <> ", have you seen Chainweb.Test.TestVersions.legalizeTestVersion?" +-- | Versions known to us by name. knownVersions :: [ChainwebVersion] knownVersions = [mainnet, testnet, devnet] +-- | Look up a known version by name, usually with `m` instantiated to some +-- configuration parser monad. findKnownVersion :: MonadFail m => ChainwebVersionName -> m ChainwebVersion findKnownVersion vn = case find (\v -> _versionName v == vn) knownVersions of diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index ff64106ef3..c074f41918 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -1,8 +1,9 @@ +{-# language LambdaCase #-} {-# language NumericUnderscores #-} -{-# language PatternSynonyms #-} {-# language OverloadedStrings #-} -{-# language ViewPatterns #-} +{-# language PatternSynonyms #-} {-# language QuasiQuotes #-} +{-# language ViewPatterns #-} module Chainweb.Version.Testnet(testnet, pattern Testnet04) where @@ -15,6 +16,7 @@ import Chainweb.ChainId import Chainweb.Difficulty import Chainweb.Graph import Chainweb.Time +import Chainweb.Utils import Chainweb.Utils.Rule import Chainweb.Version import P2P.BootstrapNodes @@ -77,6 +79,7 @@ import qualified Chainweb.BlockHeader.Genesis.TestnetNPayload as PNN testnet20InitialHashTarget :: HashTarget testnet20InitialHashTarget = HashTarget 0x000000001b9bf15be43824bae4c4f17722572883f7b53ed2e8c6ba9596249235 +-- | The block height of the 20-chain transition. to20ChainsTestnet :: BlockHeight to20ChainsTestnet = 332_604 -- 2020-07-28 16:00:00 @@ -88,33 +91,34 @@ testnet :: ChainwebVersion testnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000007 , _versionName = ChainwebVersionName "testnet04" - , _versionForks = HM.fromList - [ (Chainweb217Pact, AllChains 2_777_367) -- 2022-12-01 12:00:00+00:00 - , (Chainweb216Pact, AllChains 2_516_739) -- 2022-09-01 12:00:00+00:00 - , (Chainweb215Pact, AllChains 2_295_437) -- 2022-06-16T12:00:00+00:00 - , (Chainweb214Pact, AllChains 2_134_331) -- 2022-04-21T12:00:00Z - , (Chainweb213Pact, AllChains 1_974_556) -- 2022-02-25 00:00:00 - , (Pact420, AllChains 1_862_000) -- 2021-06-19T03:34:05 - , (Pact4Coin3, AllChains 1_261_000) -- 2021-06-17T15:54:14 - , (CoinV2, onChains $ concat + , _versionForks = tabulateHashMap $ \case + SlowEpoch -> AllChains 0 + OldTargetGuard -> AllChains 0 + SkipFeatureFlagValidation -> AllChains 0 + OldDAGuard -> AllChains 318_204 -- ~ 2020-07-23 16:00:00 + Vuln797Fix -> AllChains 0 + PactBackCompat_v16 -> AllChains 0 + SkipTxTimingValidation -> AllChains 1 + ModuleNameFix -> AllChains 2 + ModuleNameFix2 -> AllChains 289_966 -- ~ 2020-07-13 + PactEvents -> AllChains 660_000 + SPVBridge -> AllChains 820_000 -- 2021-01-14T17:12:02 + EnforceKeysetFormats -> AllChains 1_701_000 -- 2021-11-18T17:54:36 + CheckTxHash -> AllChains 1_889_000 -- 2022-01-24T04:19:24 + Pact44NewTrans -> AllChains 2_500_369 -- Todo: add date + Pact420 -> AllChains 1_862_000 -- 2021-06-19T03:34:05 + + CoinV2 -> onChains $ concat [ [(unsafeChainId i, BlockHeight 1) | i <- [0..9]] , [(unsafeChainId i, BlockHeight 337_000) | i <- [10..19]] - ]) - , (SlowEpoch, AllChains 0) - , (OldTargetGuard, AllChains 0) - , (SkipFeatureFlagValidation, AllChains 0) - , (OldDAGuard, AllChains 318_204) -- ~ 2020-07-23 16:00:00 - , (Vuln797Fix, AllChains 0) - , (PactBackCompat_v16, AllChains 0) - , (SkipTxTimingValidation, AllChains 1) - , (ModuleNameFix, AllChains 2) - , (ModuleNameFix2, AllChains 289_966) -- ~ 2020-07-13 - , (PactEvents, AllChains 660_000) - , (SPVBridge, AllChains 820_000) -- 2021-01-14T17:12:02 - , (EnforceKeysetFormats, AllChains 1_701_000) -- 2021-11-18T17:54:36 - , (CheckTxHash, AllChains 1_889_000) -- 2022-01-24T04:19:24 - , (Pact44NewTrans, AllChains 2_500_369) -- Todo: add date - ] + ] + Pact4Coin3 -> AllChains 1_261_000 -- 2021-06-17T15:54:14 + Chainweb213Pact -> AllChains 1_974_556 -- 2022-02-25 00:00:00 + Chainweb214Pact -> AllChains 2_134_331 -- 2022-04-21T12:00:00Z + Chainweb215Pact -> AllChains 2_295_437 -- 2022-06-16T12:00:00+00:00 + Chainweb216Pact -> AllChains 2_516_739 -- 2022-09-01 12:00:00+00:00 + Chainweb217Pact -> AllChains 2_777_367 -- 2022-12-01 12:00:00+00:00 + , _versionGraphs = (to20ChainsTestnet, twentyChainGraph) `Above` End petersonChainGraph diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 53709146eb..19f9362d6e 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -75,10 +75,9 @@ legalizeTestVersion f = unsafePerformIO $ do registerVersion v return v --- edtodo: document --- all chainweb versions used in tests *must* be included in this list to be assigned +-- | All chainweb versions used in tests *must* be included in this list to be assigned -- a version code, and also registered via legalizeTestVersion into the version registry. --- failure to do so will result in runtime errors. +-- Failure to do so will result in runtime errors. testRegistry :: [ChainwebVersionName] testRegistry = concat [ [ _versionName $ fastForkingCpmTestVersion' (knownChainGraph g) undefined diff --git a/test/ChainwebTests.hs b/test/ChainwebTests.hs index 815090af01..d33511f4fa 100644 --- a/test/ChainwebTests.hs +++ b/test/ChainwebTests.hs @@ -63,6 +63,8 @@ import Chainweb.Test.Utils withToyDB) import qualified Chainweb.Test.Version (tests) import qualified Chainweb.Test.Chainweb.Utils.Paging (properties) +import Chainweb.Version.Development +import Chainweb.Version.Registry import Chainweb.Storage.Table.RocksDB @@ -73,16 +75,17 @@ import qualified P2P.Test.TaskQueue (properties) import qualified P2P.Test.Node (properties) main :: IO () -main = +main = do + registerVersion Development withTempRocksDb "chainweb-tests" $ \rdb -> - withToyDB rdb toyChainId $ \h0 db -> - defaultMainWithIngredients (consoleAndJsonReporter : defaultIngredients) - $ adjustOption adj - $ testGroup "Chainweb Tests" . schedule Sequential - $ pactTestSuite rdb - : mempoolTestSuite db h0 - : rosettaTestSuite rdb - : suite rdb + withToyDB rdb toyChainId $ \h0 db -> + defaultMainWithIngredients (consoleAndJsonReporter : defaultIngredients) + $ adjustOption adj + $ testGroup "Chainweb Tests" . schedule Sequential + $ pactTestSuite rdb + : mempoolTestSuite db h0 + : rosettaTestSuite rdb + : suite rdb where adj NoTimeout = Timeout (1_000_000 * 60 * 10) "10m" adj x = x diff --git a/tools/cwtool/CwTool.hs b/tools/cwtool/CwTool.hs index 41bf49df81..d80f8199cc 100644 --- a/tools/cwtool/CwTool.hs +++ b/tools/cwtool/CwTool.hs @@ -1,6 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Main where +import Chainweb.Version.Development +import Chainweb.Version.Registry + import System.Environment import System.Exit import Text.Printf @@ -18,6 +21,7 @@ import qualified TxSimulator main :: IO () main = do + registerVersion Development args <- getArgs case args of [] -> printHelp topLevelCommands From 0519d27aadd0dadb01d13546178d3199111881fc Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 22:52:23 -0500 Subject: [PATCH 05/91] Removals --- src/Chainweb/Utils.hs | 62 ----------------------- src/Chainweb/Version.hs | 108 +++------------------------------------- 2 files changed, 8 insertions(+), 162 deletions(-) diff --git a/src/Chainweb/Utils.hs b/src/Chainweb/Utils.hs index 14cb69edbf..297e41bdfc 100644 --- a/src/Chainweb/Utils.hs +++ b/src/Chainweb/Utils.hs @@ -67,12 +67,7 @@ module Chainweb.Utils , (==>) , keySet , tabulateHashMap -, minimumsOf -, minimumsByOf , maxBy -, minBy -, allEqOn -, roundBy , unlessM , whenM , ebool_ @@ -131,7 +126,6 @@ module Chainweb.Utils , (???) , fromEitherM , InternalInvariantViolation(..) -, eatIOExceptions -- ** Synchronous Exceptions , catchSynchronous @@ -193,7 +187,6 @@ module Chainweb.Utils , suncurry , suncurry3 , uncurry3 -, rwipe3 , _T2 , _T3 @@ -365,24 +358,6 @@ keySet = HS.fromMap . set each () tabulateHashMap :: (Enum a, Bounded a, Hashable a, Eq a) => (a -> b) -> HM.HashMap a b tabulateHashMap f = HM.fromList [ (a, f a) | a <- [minBound..maxBound] ] --- | The the minimum elements of a list. --- -minimumsOf :: Ord a => Getting (Endo (Endo [a])) s a -> s -> [a] -minimumsOf l = minimumsByOf l compare -{-# INLINE minimumsOf #-} - --- | The the minimum elements of a list by some comparision function. --- -minimumsByOf :: Getting (Endo (Endo [a])) s a -> (a -> a -> Ordering) -> s -> [a] -minimumsByOf l cmp = foldlOf' l mf [] - where - mf [] !y = [y] - mf x@(h:_) !y = case cmp h y of - EQ -> y:x - GT -> [y] - LT -> x -{-# INLINE minimumsByOf #-} - -- | The maximum of two value by some comparision function. -- maxBy :: (a -> a -> Ordering) -> a -> a -> a @@ -391,23 +366,6 @@ maxBy cmp a b = case cmp a b of _ -> a {-# INLINE maxBy #-} --- | The minimum of two value by some comparision function. --- -minBy :: (a -> a -> Ordering) -> a -> a -> a -minBy cmp a b = case cmp a b of - GT -> b - _ -> a -{-# INLINE minBy #-} - --- | Checks that all elements of foldable structure are equal under the given --- mapping. --- -allEqOn :: Foldable f => Eq b => (a -> b) -> f a -> Bool -allEqOn p f = case toList f of - [] -> True - (h:t) -> all (== p h) $ p <$> t -{-# INLINEABLE allEqOn #-} - -- | A version of 'unless' with a monadic predicate. -- unlessM :: Monad m => m Bool -> m () -> m () @@ -423,13 +381,6 @@ whenM c a = c >>= flip when a ebool_ :: e -> Bool -> Either e () ebool_ e = bool (Left e) (Right ()) --- | Round an integral `n` up to the nearest multiple of --- an integral `m` --- -roundBy :: Integral a => a -> a -> a -roundBy n m = ((n `div` m) + 1) * m -{-# INLINE roundBy #-} - -- | Elide 'semialign' import with this simple Vector-specialized version. -- O(n)-ish -- O(min (m,n) + 2*max(m-n,n-m)) alignWithV :: (These a b -> c) -> V.Vector a -> V.Vector b -> V.Vector c @@ -870,15 +821,6 @@ newtype InternalInvariantViolation = InternalInvariantViolation T.Text instance Exception InternalInvariantViolation --- | Catch and strictly evaluate any 'IOException's. --- --- This function should be used with great care because operation may silently --- fail without leaving a trace. This can hide issues in the code making them --- very difficult to debug. --- -eatIOExceptions :: IO () -> IO () -eatIOExceptions = handle $ \(e :: IOException) -> void $ evaluate e - -- | Catch and handle exception that are not contained in 'SomeAsyncException'. -- catchSynchronous @@ -1294,10 +1236,6 @@ suncurry3 :: (a -> b -> c -> d) -> T3 a b c -> d suncurry3 k (T3 a b c) = k a b c {-# INLINE suncurry3 #-} -rwipe3 :: T3 a b c -> T2 b c -rwipe3 (T3 _ b c) = T2 b c -{-# INLINE rwipe3 #-} - _T2 :: Iso (T2 a b) (T2 s t) (a,b) (s,t) _T2 = iso (\(T2 a b) -> (a,b)) (uncurry T2) {-# INLINE _T2 #-} diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 20b8515d44..dcd07bae38 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -34,7 +34,8 @@ -- Maintainer: Lars Kuhtz -- Stability: experimental -- --- Properties of Chainweb Versions +-- Properties of Chainweb Versions; properties which should be consistent +-- between all nodes running on the same network. -- module Chainweb.Version ( Fork(..) @@ -74,9 +75,6 @@ module Chainweb.Version , domainAddr2PeerInfo , encodeChainwebVersionCode , decodeChainwebVersionCode - -- , chainwebVersionFromText - -- , chainwebVersionToText - -- , chainwebVersionId -- * Properties of Chainweb Version -- ** POW @@ -283,9 +281,10 @@ newtype ChainwebVersionCode = data Upgrade = Upgrade { _upgradeTransactions :: [ChainwebTransaction] , _legacyUpgradeIsPrecocious :: Bool - -- ^ when set to `True`, the upgrade transactions are executed using the forks of the subsequent - -- block, rather than the block the upgrade transactions are included in. - -- do not use this for new upgrades unless you are sure you need it. + -- ^ when set to `True`, the upgrade transactions are executed using the + -- forks of the next block, rather than the block the upgrade transactions + -- are included in. do not use this for new upgrades unless you are sure + -- you need it, this mostly exists for old upgrades. } deriving stock (Generic) deriving anyclass (NFData) @@ -381,30 +380,6 @@ emptyPayload = PayloadWithOutputs mempty miner coinbase h i o miner = MinerData $ encodeToByteString noMiner coinbase = noCoinbaseOutput --- -- | This function and its dual `fromChainwebVersionId` are used to efficiently --- -- serialize a `ChainwebVersion` and its associated internal `ChainGraph` value. --- -- __This function must be injective (one-to-one)!__ The scheme is as follows: --- -- --- -- * Production `ChainwebVersion`s start from @0x00000001@ and count upwards. --- -- Their value must be less than @0x8000000@, but this limit is unlikely to --- -- ever be reached. --- -- --- -- * `ChainwebVersion`s for testing begin at @0x80000000@, as can be seen in --- -- `toTestChainwebVersion`. This value is combined (via `.|.`) with the --- -- "code" of their associated `ChainGraph` (as seen in `graphToCode`). Such --- -- codes start at @0x00010000@ and count upwards. --- -- --- -- chainwebVersionId :: ChainwebVersion dc -> Word32 --- -- chainwebVersionId v@Test{} = toTestChainwebVersionId v --- -- chainwebVersionId v@TimedConsensus{} = toTestChainwebVersionId v --- -- chainwebVersionId v@PowConsensus{} = toTestChainwebVersionId v --- -- chainwebVersionId v@TimedCPM{} = toTestChainwebVersionId v --- -- chainwebVersionId v@FastTimedCPM{} = toTestChainwebVersionId v --- -- chainwebVersionId Development{} = 0x00000001 --- -- chainwebVersionId Testnet04 = 0x00000007 --- -- chainwebVersionId Mainnet01 = 0x00000005 --- -- {-# INLINABLE chainwebVersionId #-} - encodeChainwebVersionCode :: ChainwebVersionCode -> Put encodeChainwebVersionCode = putWord32le . getChainwebVersionCode @@ -420,69 +395,6 @@ instance HasTextRepresentation ChainwebVersionName where toText = getChainwebVersionName fromText = pure . ChainwebVersionName --- -- -------------------------------------------------------------------------- -- --- -- Test instances --- -- --- -- The code in this section must not be called in production. --- -- - --- data GraphPos = P1 | P2 deriving (Bounded, Enum) - --- graphToCodeN :: GraphPos -> KnownGraph -> Word32 --- graphToCodeN p g = shiftL (graphToCode g) (4 * (4 + fromEnum p)) --- where --- graphToCode :: KnownGraph -> Word32 --- graphToCode Singleton = 0x00000001 --- graphToCode Pair = 0x00000002 --- graphToCode Triangle = 0x00000003 --- graphToCode Peterson = 0x00000004 --- graphToCode Twenty = 0x00000005 --- graphToCode HoffmanSingleton = 0x00000006 - --- codeToGraphN :: HasCallStack => GraphPos -> Word32 -> KnownGraph --- codeToGraphN p c = codeToGraph (shiftR c (4 * (4 + fromEnum p)) .&. 0x0000000f) --- where --- codeToGraph :: HasCallStack => Word32 -> KnownGraph --- codeToGraph 0x00000001 = Singleton --- codeToGraph 0x00000002 = Pair --- codeToGraph 0x00000003 = Triangle --- codeToGraph 0x00000004 = Peterson --- codeToGraph 0x00000005 = Twenty --- codeToGraph 0x00000006 = HoffmanSingleton --- codeToGraph _ = error "Unknown Graph Code" - --- -- toTestChainwebVersionId :: HasCallStack => ChainwebVersion dc -> Word32 --- -- toTestChainwebVersionId (Test g) = 0x80000000 --- -- .|. graphToCodeN P1 (view chainGraphKnown g) --- -- toTestChainwebVersionId (TimedConsensus g1 g2) = 0x80000001 --- -- .|. graphToCodeN P1 (view chainGraphKnown g1) --- -- .|. graphToCodeN P2 (view chainGraphKnown g2) --- -- toTestChainwebVersionId (PowConsensus g) = 0x80000002 --- -- .|. graphToCodeN P1 (view chainGraphKnown g) --- -- toTestChainwebVersionId (TimedCPM g) = 0x80000003 --- -- .|. graphToCodeN P1 (view chainGraphKnown g) --- -- toTestChainwebVersionId (FastTimedCPM g) = 0x80000004 --- -- .|. graphToCodeN P1 (view chainGraphKnown g) --- -- toTestChainwebVersionId Development{} = --- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" --- -- toTestChainwebVersionId Testnet04 = --- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" --- -- toTestChainwebVersionId Mainnet01 = --- -- error "Illegal ChainwebVersion passed to toTestChainwebVersion" - --- -- fromTestChainwebVersionId :: HasCallStack => Word32 -> ChainwebVersionCode --- -- fromTestChainwebVersionId c = case v of --- -- 0x80000000 -> Test (knownChainGraph $ codeToGraphN P1 g) --- -- 0x80000001 -> TimedConsensus --- -- (knownChainGraph $ codeToGraphN P1 g) --- -- (knownChainGraph $ codeToGraphN P2 g) --- -- 0x80000002 -> PowConsensus (knownChainGraph $ codeToGraphN P1 g) --- -- 0x80000003 -> TimedCPM (knownChainGraph $ codeToGraphN P1 g) --- -- 0x80000004 -> FastTimedCPM (knownChainGraph $ codeToGraphN P1 g) --- -- _ -> error "Unknown ChainwebVersion Code" --- -- where --- -- (v, g) = (0xf000ffff .&. c, 0x0fff0000 .&. c) - -- -- -------------------------------------------------------------------------- -- -- -- Type level ChainwebVersion @@ -579,17 +491,13 @@ forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) -- will have taken effect: forks, upgrade transactions, or graph changes. latestBehaviorAt :: ChainwebVersion -> BlockHeight latestBehaviorAt v = - foldlOf' changes maxBlockHeight 0 v + 1 + foldlOf' changes max 0 v + 1 where changes = fold [ versionForks . folded . folded , versionUpgrades . folded . ifolded . asIndex , versionGraphs . to ruleHead . _1 . _Just - ] - maxBlockHeight x y - | x == maxBound = y - | y == maxBound = x - | otherwise = max x y + ] . filtered (/= maxBound) mkChainId :: (MonadThrow m, HasChainwebVersion v) From 932ebd3ad400c1cd2c83cf9dec2666bd8d1dc6bb Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 22:55:26 -0500 Subject: [PATCH 06/91] Fix build --- src/Chainweb/Pact/Backend/Utils.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/Pact/Backend/Utils.hs b/src/Chainweb/Pact/Backend/Utils.hs index c9433d5a18..c4c74f448b 100644 --- a/src/Chainweb/Pact/Backend/Utils.hs +++ b/src/Chainweb/Pact/Backend/Utils.hs @@ -71,7 +71,7 @@ import Data.Foldable import Data.String import qualified Data.Text as T import qualified Data.Text.Encoding as T -import Database.SQLite3.Direct as SQ3 +import Database.SQLite3.Direct as SQ3 hiding (open2) import Prelude hiding (log) From ddacf98d445214f569bf6e01895b39d8fa05d58e Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 23:01:03 -0500 Subject: [PATCH 07/91] fix haddock --- src/Chainweb/Version/Guards.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 00028e1676..01870cac81 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -15,11 +15,10 @@ -- chainweb versions. -- -module Chainweb.Version.Guards +module Chainweb.Version.Guards + ( -- ** Payload Validation Guards - ( vuln797Fix - -- , coinV2Upgrade - -- , to20ChainRebalance + vuln797Fix , pactBackCompat_v16 , skipTxTimingValidation , enableModuleNameFix From 4675df6d3664cf7f43a0eb115d32b55dd4937e20 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 23:04:24 -0500 Subject: [PATCH 08/91] Fix build add docs --- src/Chainweb/Utils.hs | 5 +---- src/Chainweb/Version/Guards.hs | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Chainweb/Utils.hs b/src/Chainweb/Utils.hs index 297e41bdfc..0c56e7dc29 100644 --- a/src/Chainweb/Utils.hs +++ b/src/Chainweb/Utils.hs @@ -218,8 +218,7 @@ import Control.Concurrent.Async import Control.Concurrent.MVar import Control.Concurrent.TokenBucket import Control.DeepSeq -import Control.Exception - (IOException, SomeAsyncException(..), evaluate) +import Control.Exception (SomeAsyncException(..)) import Control.Lens hiding ((.=)) import Control.Monad import Control.Monad.Catch hiding (bracket) @@ -239,12 +238,10 @@ import qualified Data.ByteString.Base64.URL as B64U import qualified Data.ByteString.Lazy as BL import qualified Data.Csv as CSV import Data.Decimal -import Data.Foldable import Data.Functor.Of import Data.Hashable import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS -import Data.Monoid (Endo) import Data.Proxy import Data.String (IsString(..)) import qualified Data.Text as T diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 01870cac81..b892c123a9 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -14,6 +14,10 @@ -- Functions which dictate changes in block validation at different BlockHeights, based on -- chainweb versions. -- +-- Changes either activate at a certain block height and for all subsequent blocks, +-- activate for all subsequent blocks after a certain block height, +-- or activate for all previous blocks before a certain block height. +-- module Chainweb.Version.Guards ( From 9f2d4af24280dc14c825e9a51260b9d68014f74a Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 1 Mar 2023 23:15:28 -0500 Subject: [PATCH 09/91] fix haddock again --- src/Chainweb/Version/Registry.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index 75121c50c2..a8b41da08f 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -62,7 +62,7 @@ registerVersion v = do -- | Look up a version in the registry by code. lookupVersionByCode :: HasCallStack => ChainwebVersionCode -> ChainwebVersion lookupVersionByCode code - -- | these two cases exist to ensure that the mainnet and testnet versions + -- these two cases exist to ensure that the mainnet and testnet versions -- cannot be accidentally replaced and are the most performant to look up. -- registering them is still allowed, as long as they are not conflicting. | code == _versionCode mainnet = mainnet From 1257d89a53655dd0e1f783fc51c9b9b2c89dae61 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 11:32:04 -0500 Subject: [PATCH 10/91] Fix other warnings --- bench/JSONEncoding.hs | 1 - src/Chainweb/Chainweb/Configuration.hs | 2 +- .../Test/Pact/ModuleCacheOnRestart.hs | 2 +- test/Chainweb/Test/Pact/RemotePactTest.hs | 27 +++++++++---------- test/Chainweb/Test/Rosetta/RestAPI.hs | 2 -- test/Chainweb/Test/TestVersions.hs | 17 +++++++----- test/SlowTests.hs | 1 - tools/genconf/GenConf.hs | 1 - tools/run-nodes/RunNodes.hs | 1 - 9 files changed, 24 insertions(+), 30 deletions(-) diff --git a/bench/JSONEncoding.hs b/bench/JSONEncoding.hs index c3f38c9987..f0c07c2732 100644 --- a/bench/JSONEncoding.hs +++ b/bench/JSONEncoding.hs @@ -44,7 +44,6 @@ import Chainweb.Payload import Chainweb.RestAPI.NodeInfo import Chainweb.Test.Orphans.Internal import Chainweb.Utils.Paging -import Chainweb.Version import Chainweb.Version.Mainnet -- -------------------------------------------------------------------------- -- diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index e82252ab7f..fe8626c698 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -170,7 +170,7 @@ instance FromJSON (ThrottlingConfig -> ThrottlingConfig) where <*< throttlingLocalRate ..: "local" % o -- -------------------------------------------------------------------------- -- --- Cut Coniguration +-- Cut Configuration data ChainDatabaseGcConfig = GcNone diff --git a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs index feb9675e2c..7270bdbf5d 100644 --- a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs +++ b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs @@ -50,7 +50,7 @@ import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Utils import Chainweb.Test.Pact.Utils import Chainweb.Test.TestVersions(fastForkingCpmTestVersion) -import Chainweb.Utils (T2(..), fromJuste) +import Chainweb.Utils (T2(..)) import Chainweb.Version import Chainweb.WebBlockHeaderDB diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index a1cc045a45..43a5e14d95 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -34,7 +34,6 @@ import Control.Lens import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class -import Control.Retry import qualified Data.Aeson as A import Data.Aeson.Lens hiding (values) @@ -44,7 +43,6 @@ import Data.Decimal import Data.Default (def) import Data.Foldable (toList) import qualified Data.HashMap.Strict as HashMap -import qualified Data.HashSet as HashSet import qualified Data.List as L import qualified Data.List.NonEmpty as NEL import qualified Data.Map.Strict as M @@ -76,10 +74,7 @@ import Pact.Types.Term -- internal modules -import Chainweb.BlockHeight import Chainweb.ChainId -import Chainweb.Cut.CutHashes -import Chainweb.CutDB.RestAPI.Client import Chainweb.Graph import Chainweb.Mempool.Mempool import Chainweb.Pact.RestAPI.Client @@ -599,18 +594,20 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do Left e -> assertFailure $ "test failure: " <> show e Right cr -> assertEqual "00 expect /local allocation balance" accountInfo (resultOf cr) + -- edtodo: be more principled about `try`? step "negative allocation test: allocation01 release" - batch0 <- mkSingletonBatch iot allocation01KeyPair tx2 n2 (pm "allocation01") Nothing + do + batch0 <- mkSingletonBatch iot allocation01KeyPair tx2 n2 (pm "allocation01") Nothing - testCaseStep "sendApiClient: submit allocation release request" - cr <- local sid cenv (NEL.head $ _sbCmds batch0) - - case resultOf cr of - Left e -> do - assertBool "expect negative allocation test failure" - $ T.isInfixOf "Failure: Tx Failed: funds locked" - $ sshow e - _ -> assertFailure "unexpected pact result success in negative allocation test" + testCaseStep "sendApiClient: submit allocation release request" + cr <- local sid cenv (NEL.head $ _sbCmds batch0) + + case resultOf cr of + Left e -> do + assertBool "expect negative allocation test failure" + $ T.isInfixOf "Failure: Tx Failed: funds locked" + $ sshow e + _ -> assertFailure "unexpected pact result success in negative allocation test" step "positive key-rotation test: allocation2" r <- try @IO @PactTestFailure $ do diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 024e608317..f32aa197d3 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -10,8 +10,6 @@ module Chainweb.Test.Rosetta.RestAPI ( tests ) where - -import Debug.Trace import Control.Concurrent.Async import Control.Concurrent.MVar import Control.Lens diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 19f9362d6e..7e78e9c34e 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -16,14 +16,10 @@ module Chainweb.Test.TestVersions ) where import Control.Lens hiding (elements) -import Data.Bits -import Data.Foldable -import Data.Function import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.List as List -import Data.Word import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload as TN0 import qualified Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload as TNN @@ -69,7 +65,9 @@ testBootstrapPeerInfos = } } -legalizeTestVersion :: (ChainwebVersion -> ChainwebVersion) -> ChainwebVersion +type VersionBuilder = ChainwebVersion -> ChainwebVersion + +legalizeTestVersion :: VersionBuilder -> ChainwebVersion legalizeTestVersion f = unsafePerformIO $ do let v = f v registerVersion v @@ -98,7 +96,7 @@ testRegistry = concat ] ] -testVersionTemplate :: ChainwebVersion -> ChainwebVersion +testVersionTemplate :: VersionBuilder testVersionTemplate v = v & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + 0x80000000) & versionHeaderBaseSizeBytes .~ 318 - 110 @@ -136,6 +134,7 @@ fastForks = HM.fromList barebonesTestVersion :: ChainGraph -> ChainwebVersion barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) +barebonesTestVersion' :: ChainGraph -> VersionBuilder barebonesTestVersion' g v = testVersionTemplate v & versionWindow .~ Nothing @@ -161,7 +160,7 @@ barebonesTestVersion' g v = , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) ] -cpmTestVersion :: ChainGraph -> ChainwebVersion -> ChainwebVersion +cpmTestVersion :: ChainGraph -> VersionBuilder cpmTestVersion g v = v & versionWindow .~ Nothing & versionBlockRate .~ BlockRate (Micros 200_000) @@ -191,6 +190,7 @@ cpmTestVersion g v = v slowForkingCpmTestVersion :: ChainGraph -> ChainwebVersion slowForkingCpmTestVersion g = legalizeTestVersion (slowForkingCpmTestVersion' g) +slowForkingCpmTestVersion' :: ChainGraph -> VersionBuilder slowForkingCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("slowfork-CPM-" <> toText g) @@ -222,6 +222,7 @@ slowForkingCpmTestVersion' g v = fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion fastForkingCpmTestVersion g = legalizeTestVersion (fastForkingCpmTestVersion' g) +fastForkingCpmTestVersion' :: ChainGraph -> VersionBuilder fastForkingCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("fastfork-CPM-" <> toText g) @@ -230,6 +231,7 @@ fastForkingCpmTestVersion' g v = noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion noBridgeCpmTestVersion g = legalizeTestVersion (noBridgeCpmTestVersion' g) +noBridgeCpmTestVersion' :: ChainGraph -> VersionBuilder noBridgeCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) @@ -238,6 +240,7 @@ noBridgeCpmTestVersion' g v = timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion timedConsensusVersion g1 g2 = legalizeTestVersion (timedConsensusVersion' g1 g2) +timedConsensusVersion' :: ChainGraph -> ChainGraph -> VersionBuilder timedConsensusVersion' g1 g2 v = testVersionTemplate v & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) diff --git a/test/SlowTests.hs b/test/SlowTests.hs index b381189db6..85bd784023 100644 --- a/test/SlowTests.hs +++ b/test/SlowTests.hs @@ -21,7 +21,6 @@ import Test.Tasty import Chainweb.Graph import qualified Chainweb.Test.MultiNode import Chainweb.Test.TestVersions -import Chainweb.Version import qualified Network.X509.SelfSigned.Test diff --git a/tools/genconf/GenConf.hs b/tools/genconf/GenConf.hs index 88b7e5c193..cf433e253b 100644 --- a/tools/genconf/GenConf.hs +++ b/tools/genconf/GenConf.hs @@ -22,7 +22,6 @@ import Chainweb.Chainweb import Chainweb.Chainweb.Configuration import Chainweb.HostAddress import Chainweb.Miner.Config -import Chainweb.Version import Chainweb.Version.Mainnet import P2P.Node.Configuration diff --git a/tools/run-nodes/RunNodes.hs b/tools/run-nodes/RunNodes.hs index fbaab75cb4..825225551f 100644 --- a/tools/run-nodes/RunNodes.hs +++ b/tools/run-nodes/RunNodes.hs @@ -10,7 +10,6 @@ import Chainweb.Version.Registry import Control.Concurrent import Control.Concurrent.Async -import Control.Error.Util (note) import Control.Exception import qualified Data.Text as T From f529baed3dc69b2462e958dd8829ac32b44ed905 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 12:51:45 -0500 Subject: [PATCH 11/91] Eq for upgrades --- src/Chainweb/Version.hs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index dcd07bae38..4c1d26d4d7 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -286,7 +286,7 @@ data Upgrade = Upgrade -- are included in. do not use this for new upgrades unless you are sure -- you need it, this mostly exists for old upgrades. } - deriving stock (Generic) + deriving stock (Generic, Eq) deriving anyclass (NFData) upgrade :: [ChainwebTransaction] -> Upgrade @@ -331,6 +331,8 @@ instance Ord ChainwebVersion where , _versionName v `compare` _versionName v' , _versionGraphs v `compare` _versionGraphs v' , _versionForks v `compare` _versionForks v' + -- upgrades cannot be ordered because Payload in Pact cannot be ordered + -- , _versionUpgrades v `compare` _versionUpgrades v' , _versionBlockRate v `compare` _versionBlockRate v' , _versionWindow v `compare` _versionWindow v' , _versionHeaderBaseSizeBytes v `compare` _versionHeaderBaseSizeBytes v' @@ -341,7 +343,10 @@ instance Ord ChainwebVersion where ] instance Eq ChainwebVersion where - v == v' = compare v v' == EQ + v == v' = and + [ compare v v' == EQ + , _versionUpgrades v == _versionUpgrades v' + ] data Cheats = Cheats { _disablePow :: Bool From 067831e805b9dcca1f37fdfa7f0b0462b0ec50e3 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 19:04:55 -0500 Subject: [PATCH 12/91] Move maxBlockGasLimit --- src/Chainweb/Chainweb.hs | 1 + src/Chainweb/Version.hs | 15 +-------------- src/Chainweb/Version/Guards.hs | 9 +++++++++ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Chainweb/Chainweb.hs b/src/Chainweb/Chainweb.hs index 91ec247f43..d0eb87253c 100644 --- a/src/Chainweb/Chainweb.hs +++ b/src/Chainweb/Chainweb.hs @@ -170,6 +170,7 @@ import Chainweb.Transaction import Chainweb.Utils import Chainweb.Utils.RequestLog import Chainweb.Version +import Chainweb.Version.Guards import Chainweb.WebBlockHeaderDB import Chainweb.WebPactExecutionService diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 00be0a330e..7bc10ac91c 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -1,3 +1,4 @@ +-- edtodo redo docs here {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} @@ -81,7 +82,6 @@ module Chainweb.Version , BlockRate(..) , WindowWidth(..) -- ** Payload Validation Parameters - , maxBlockGasLimit -- * Typelevel ChainwebVersion , ChainwebVersionT(..) @@ -552,16 +552,3 @@ chainGraphAt_ = chainGraphAt . _chainwebVersion instance HasChainGraph (ChainwebVersion, BlockHeight) where _chainGraph = uncurry chainGraphAt - -maxBlockGasLimit :: ChainwebVersion -> BlockHeight -> Maybe Natural -maxBlockGasLimit v bh = case measureRule bh $ _versionMaxBlockGasLimit v of - Bottom limit -> limit - Top (_, limit) -> limit - Between (_, limit) _ -> limit - --- edtodo --- chainweb218Pact :: ChainwebVersion -> BlockHeight -> Bool --- chainweb218Pact Testnet04 = (>= 3_038_343) -- 2023-03-02 12:00:00+00:00 --- chainweb218Pact Development = (>= 500) --- chainweb218Pact (FastTimedCPM g) | g == petersonChainGraph = (> 60) --- chainweb218Pact _ = (> 24) \ No newline at end of file diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 41b0876516..fce83807ae 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -42,6 +42,7 @@ module Chainweb.Version.Guards , chainweb218Pact , pact44NewTrans , pactParserVersion + , maxBlockGasLimit -- ** BlockHeader Validation Guards , slowEpochGuard @@ -51,11 +52,13 @@ module Chainweb.Version.Guards ) where import Control.Lens +import Numeric.Natural import Chainweb.BlockHeight import Chainweb.ChainId import Chainweb.Transaction import Chainweb.Version +import Chainweb.Utils.Rule getForkHeight :: Fork -> ChainwebVersion -> ChainId -> BlockHeight getForkHeight fork v cid = v ^?! versionForks . at fork . _Just . onChain cid @@ -123,3 +126,9 @@ pactParserVersion :: ChainwebVersion -> ChainId -> BlockHeight -> PactParserVers pactParserVersion v cid bh | chainweb213Pact v cid bh = PactParserChainweb213 | otherwise = PactParserGenesis + +maxBlockGasLimit :: ChainwebVersion -> BlockHeight -> Maybe Natural +maxBlockGasLimit v bh = case measureRule bh $ _versionMaxBlockGasLimit v of + Bottom limit -> limit + Top (_, limit) -> limit + Between (_, limit) _ -> limit From 96d062be3316d0cd3752145a40787e9c9c314135 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 19:24:20 -0500 Subject: [PATCH 13/91] Delete old testnet history case in ExecBlock --- src/Chainweb/Pact/PactService/ExecBlock.hs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Chainweb/Pact/PactService/ExecBlock.hs b/src/Chainweb/Pact/PactService/ExecBlock.hs index bd9dbfbe75..9433ac75af 100644 --- a/src/Chainweb/Pact/PactService/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/ExecBlock.hs @@ -249,10 +249,6 @@ validateChainwebTxs logger v cid cp txValidationTime bh txs doBuyGas checkTxSigs :: ChainwebTransaction -> IO (Either InsertError ChainwebTransaction) checkTxSigs t | assertValidateSigs hsh signers sigs = pure $ Right t - -- special case for old testnet history - -- | v == Testnet04 && not (doCheckTxHash v bh) = do - -- P.logLog logger "DEBUG" "ignored legacy invalid signature" - -- return $ Right t | otherwise = return $ Left InsertErrorInvalidSigs where hsh = P._cmdHash t From 694f21a06c8fccf5336d644760fffca43e344e1b Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 22:00:42 -0500 Subject: [PATCH 14/91] Update Utils.hs --- src/Chainweb/Pact/Backend/Utils.hs | 50 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Chainweb/Pact/Backend/Utils.hs b/src/Chainweb/Pact/Backend/Utils.hs index c9433d5a18..4fcc777bba 100644 --- a/src/Chainweb/Pact/Backend/Utils.hs +++ b/src/Chainweb/Pact/Backend/Utils.hs @@ -71,7 +71,7 @@ import Data.Foldable import Data.String import qualified Data.Text as T import qualified Data.Text.Encoding as T -import Database.SQLite3.Direct as SQ3 +import qualified Database.SQLite3.Direct as SQ3 import Prelude hiding (log) @@ -89,62 +89,66 @@ import Pact.Types.Util (AsString(..)) -- chainweb -import Chainweb.ChainId import Chainweb.Logger import Chainweb.Pact.Backend.SQLite.DirectV2 import Chainweb.Pact.Backend.Types import Chainweb.Pact.Service.Types +import Chainweb.Version import Chainweb.Utils -- -------------------------------------------------------------------------- -- --- Utf8 Encodings +-- SQ3.Utf8 Encodings -toUtf8 :: T.Text -> Utf8 -toUtf8 = Utf8 . T.encodeUtf8 +toUtf8 :: T.Text -> SQ3.Utf8 +toUtf8 = SQ3.Utf8 . T.encodeUtf8 {-# INLINE toUtf8 #-} -fromUtf8 :: Utf8 -> T.Text -fromUtf8 (Utf8 bytes) = T.decodeUtf8 bytes +fromUtf8 :: SQ3.Utf8 -> T.Text +fromUtf8 (SQ3.Utf8 bytes) = T.decodeUtf8 bytes {-# INLINE fromUtf8 #-} -toTextUtf8 :: HasTextRepresentation a => a -> Utf8 +toTextUtf8 :: HasTextRepresentation a => a -> SQ3.Utf8 toTextUtf8 = toUtf8 . toText {-# INLINE toTextUtf8 #-} -asStringUtf8 :: AsString a => a -> Utf8 +asStringUtf8 :: AsString a => a -> SQ3.Utf8 asStringUtf8 = toUtf8 . asString {-# INLINE asStringUtf8 #-} -domainTableName :: Domain k v -> Utf8 +domainTableName :: Domain k v -> SQ3.Utf8 domainTableName = asStringUtf8 -convKeySetName :: KeySetName -> Utf8 +convKeySetName :: KeySetName -> SQ3.Utf8 convKeySetName = toUtf8 . asString convModuleName :: Bool -- ^ whether to apply module name fix -> ModuleName - -> Utf8 + -> SQ3.Utf8 convModuleName False (ModuleName name _) = toUtf8 name convModuleName True mn = asStringUtf8 mn -convNamespaceName :: NamespaceName -> Utf8 +convNamespaceName :: NamespaceName -> SQ3.Utf8 convNamespaceName (NamespaceName name) = toUtf8 name -convRowKey :: RowKey -> Utf8 +convRowKey :: RowKey -> SQ3.Utf8 convRowKey (RowKey name) = toUtf8 name -convPactId :: PactId -> Utf8 +convPactId :: PactId -> SQ3.Utf8 convPactId = toUtf8 . sshow -convSavepointName :: SavepointName -> Utf8 +convSavepointName :: SavepointName -> SQ3.Utf8 convSavepointName = toTextUtf8 -- -------------------------------------------------------------------------- -- -- -callDb :: (MonadCatch m, MonadReader (BlockDbEnv SQLiteEnv) m, MonadIO m) => T.Text -> (Database -> IO b) -> m b +callDb + :: (MonadCatch m, MonadReader (BlockDbEnv SQLiteEnv) m, MonadIO m) + => T.Text + -> (SQ3.Database -> IO b) + -> m b callDb callerName action = do c <- view (bdbenvDb . sConn) res <- tryAny $ liftIO $ action c @@ -251,18 +255,18 @@ chainwebPragmas = , "page_size = 1024" ] -execMulti :: Traversable t => Database -> Utf8 -> t [SType] -> IO () +execMulti :: Traversable t => SQ3.Database -> SQ3.Utf8 -> t [SType] -> IO () execMulti db q rows = bracket (prepStmt db q) destroy $ \stmt -> do forM_ rows $ \row -> do - reset stmt >>= checkError - clearBindings stmt + SQ3.reset stmt >>= checkError + SQ3.clearBindings stmt bindParams stmt row - step stmt >>= checkError + SQ3.step stmt >>= checkError where checkError (Left e) = void $ fail $ "error during batch insert: " ++ show e checkError (Right _) = return () - destroy x = void (finalize x >>= checkError) + destroy x = void (SQ3.finalize x >>= checkError) withSqliteDb :: Logger logger @@ -344,7 +348,7 @@ withTempSQLiteConnection = withSQLiteConnection "" withInMemSQLiteConnection :: [Pragma] -> (SQLiteEnv -> IO c) -> IO c withInMemSQLiteConnection = withSQLiteConnection ":memory:" -open2 :: String -> IO (Either (Error, Utf8) Database) +open2 :: String -> IO (Either (SQ3.Error, SQ3.Utf8) SQ3.Database) open2 file = open_v2 (fromString file) (collapseFlags [sqlite_open_readwrite , sqlite_open_create , sqlite_open_fullmutex]) From 04996836e50f35bc10cf7cbf722d3f52fa0c6392 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 22:07:01 -0500 Subject: [PATCH 15/91] Update chainweb.cabal --- chainweb.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/chainweb.cabal b/chainweb.cabal index 2ac059d1b2..75ab32958b 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -381,7 +381,6 @@ library , pact >= 4.2.0.1 , pem >=0.2 , primitive >= 0.7.1.0 - , pretty-show , random >= 1.2 , rosetta >= 1.0 , safe-exceptions >= 0.1 From 7b5334f3b6faf445c53442599dd14a617adfce0a Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 22:19:53 -0500 Subject: [PATCH 16/91] Remove distributive and adjunctions deps --- chainweb.cabal | 2 -- 1 file changed, 2 deletions(-) diff --git a/chainweb.cabal b/chainweb.cabal index 75ab32958b..5d455f95ca 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -323,7 +323,6 @@ library build-depends: Decimal >= 0.4.2 - , adjunctions >= 4.4.2 , aeson >= 1.4.3 , asn1-encoding >=0.9 , asn1-types >=0.3 @@ -350,7 +349,6 @@ library , digraph >= 0.2 , direct-sqlite >= 2.3.27 , directory >= 1.3 - , distributive >= 0.6.2.1 , dlist >= 0.8 , errors >= 2.3 , ethereum >= 0.1 From 30582500ea424bfa7620098753359da77169f076 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 22:32:28 -0500 Subject: [PATCH 17/91] Remove memotrie dep --- chainweb.cabal | 2 -- 1 file changed, 2 deletions(-) diff --git a/chainweb.cabal b/chainweb.cabal index 5d455f95ca..b0ede0450b 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -564,7 +564,6 @@ test-suite chainweb-tests , text >=1.2 , time >= 1.8 , transformers >= 0.5 - , MemoTrie , unordered-containers == 0.2.15.0 , vector >= 0.12.2 , wai >= 3.2 @@ -720,7 +719,6 @@ executable cwtool , tasty-quickcheck >= 0.9 , temporary >= 1.3 , text >= 1.2 - , MemoTrie , unordered-containers == 0.2.15.0 , vector >= 0.12.2 , wai >= 3.2 From c5c958e8e23fb3cd51d9905bae44a14d2729bbcc Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 2 Mar 2023 22:39:43 -0500 Subject: [PATCH 18/91] Remove errors dep from cwtool --- chainweb.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/chainweb.cabal b/chainweb.cabal index b0ede0450b..a863feff96 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -687,7 +687,6 @@ executable cwtool , digraph >= 0.2 , direct-sqlite >= 2.3.27 , directory >= 1.3 - , errors >= 2.3 , file-embed , filepath >= 1.4 , exceptions >= 0.8 From 9b26031e938060f5cc8103f2afa1969845f918e7 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 10:24:42 -0500 Subject: [PATCH 19/91] Reorder some declarations around makeLenses --- src/Chainweb/BlockHeader.hs | 313 ++++++++++++++++++------------------ 1 file changed, 156 insertions(+), 157 deletions(-) diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 2516633538..f0d5cde14b 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -227,6 +227,161 @@ encodeEpochStartTime (EpochStartTime t) = encodeTime t decodeEpochStartTime :: Get EpochStartTime decodeEpochStartTime = EpochStartTime <$> decodeTime +-- ----------------------------------------------------------------------------- +-- Feature Flags + +newtype FeatureFlags = FeatureFlags Word64 + deriving stock (Show, Eq, Generic) + deriving anyclass (NFData) + deriving newtype (ToJSON, FromJSON) + +encodeFeatureFlags :: FeatureFlags -> Put +encodeFeatureFlags (FeatureFlags ff) = putWord64le ff + +decodeFeatureFlags :: Get FeatureFlags +decodeFeatureFlags = FeatureFlags <$> getWord64le + +instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag FeatureFlags where + type Tag FeatureFlags = 'FeatureFlagsTag + toMerkleNode = encodeMerkleInputNode encodeFeatureFlags + fromMerkleNode = decodeMerkleInputNode decodeFeatureFlags + +mkFeatureFlags :: FeatureFlags +mkFeatureFlags = FeatureFlags 0x0 + +-- -------------------------------------------------------------------------- -- +-- Block Header + +-- | BlockHeader +-- +-- Values of this type should never be constructed directly by external code. +-- Instead the 'newBlockHeader' smart constructor should be used. Once +-- constructed 'BlockHeader' values must not be modified. +-- +-- Some redundant, aggregated information is included in the block and the block +-- hash. This enables nodes to check blocks inductively with respect to existing +-- blocks without recalculating the aggregated value from the genesis block +-- onward. +-- +-- The POW hash is not included, since it can be derived from the Nonce and the +-- other fields of the 'BlockHeader'. +-- +-- /IMPORTANT/: Fields in this record must have pairwise distinct types. +-- +data BlockHeader :: Type where + BlockHeader :: + { _blockFlags :: {-# UNPACK #-} !FeatureFlags + -- ^ An 8-byte bitmask reserved for the future addition of boolean + -- "feature flags". + + , _blockCreationTime :: {-# UNPACK #-} !BlockCreationTime + -- ^ The time when the block was creates as recorded by the miner + -- of the block. The value must be strictly monotonically increasing + -- within the chain of blocks. Nodes must ignore blocks with values + -- that are in the future and reconsider a block when its value is + -- in the past. Nodes do not have to store blocks until they become + -- recent (but may do it). + -- + -- The block creation time is used to determine the block difficulty for + -- future blocks. + -- + -- Nodes are not supposed to consider the creation time when + -- choosing between two valid (this implies that creation time of a + -- block is not the future) forks. + -- + -- This creates an incentive for nodes to maintain an accurate clock + -- with respect to an (unspecified) commonly accepted time source, + -- such as the public NTP network. + -- + -- It is possible that a miner always chooses the smallest possible + -- creation time value. It is not clear what advantage a miner would + -- gain from doing so, but attack models should consider and + -- investigate such behavior. + -- + -- On the other hand miners may choose to compute forks with creation + -- time long in the future. By doing so, the difficulty on such a fork + -- would decrease allowing the miner to compute very long chains very + -- quickly. However, those chains would become valid only after a long + -- time passed and would be of low PoW weight. The algorithm for + -- computing the difficulty must ensure this strategy doesn't give + -- an advantage to an attacker that would increase the success + -- probability for an attack. + + , _blockParent :: {-# UNPACK #-} !BlockHash + -- ^ authoritative + + , _blockAdjacentHashes :: !BlockHashRecord + -- ^ authoritative + + , _blockTarget :: {-# UNPACK #-} !HashTarget + -- ^ authoritative + + , _blockPayloadHash :: {-# UNPACK #-} !BlockPayloadHash + -- ^ authoritative + + , _blockChainId :: {-# UNPACK #-} !ChainId + + , _blockWeight :: {-# UNPACK #-} !BlockWeight + -- ^ the accumulated weight of the chain. It is redundant information + -- that is subject to the inductive property that the block weight + -- of a block is the block weight of the parent plus the difficulty + -- of the block. + + , _blockHeight :: {-# UNPACK #-} !BlockHeight + -- ^ block height records the length of the chain. It is redundant + -- information and thus subject the inductive property that + -- the block height of a block is the block height of its parent + -- plus one. + + , _blockChainwebVersion :: !ChainwebVersion + -- ^ the Chainweb version is a constant for the chain. A chain + -- is uniquely identified by its genesis block. Thus this is + -- redundant information and thus subject to the inductive property + -- that the Chainweb version of a block equals the Chainweb version + -- of its parent. + + , _blockEpochStart :: {-# UNPACK #-} !EpochStartTime + -- ^ The start time of the current difficulty adjustment epoch. + -- Epochs divide the sequence of blocks in the chain into continuous + -- ranges of blocks. Each epoch is defined by the minimal block + -- height of the blocks in the epoch. + + , _blockNonce :: {-# UNPACK #-} !Nonce + -- ^ authoritative + + , _blockHash :: {-# UNPACK #-} !BlockHash + -- ^ the hash of the block. It includes all of the above block properties. + } + -> BlockHeader + deriving (Show, Generic) + deriving anyclass (NFData) + +instance Eq BlockHeader where + (==) = (==) `on` _blockHash + +instance Ord BlockHeader where + compare = compare `on` _blockHash + +instance Hashable BlockHeader where + hashWithSalt s = hashWithSalt s . _blockHash + +instance HasChainId BlockHeader where + _chainId = _blockChainId + +instance HasChainGraph BlockHeader where + _chainGraph h = _chainGraph (_blockChainwebVersion h, _blockHeight h) + +instance HasChainwebVersion BlockHeader where + _chainwebVersion = _blockChainwebVersion + +instance IsCasValue BlockHeader where + type CasKeyType BlockHeader = BlockHash + casKey = _blockHash + +type BlockHeaderCas tbl = Cas tbl BlockHeader + +makeLenses ''BlockHeader + -- | During the first epoch after genesis there are 10 extra difficulty -- adjustments. This is to account for rapidly changing total hash power in the -- early stages of the network. @@ -424,28 +579,6 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) parentIsFirstOnNewChain = _blockHeight p > 1 && _blockHeight p == genesisHeight ver cid + 1 --- ----------------------------------------------------------------------------- --- Feature Flags - -newtype FeatureFlags = FeatureFlags Word64 - deriving stock (Show, Eq, Generic) - deriving anyclass (NFData) - deriving newtype (ToJSON, FromJSON) - -encodeFeatureFlags :: FeatureFlags -> Put -encodeFeatureFlags (FeatureFlags ff) = putWord64le ff - -decodeFeatureFlags :: Get FeatureFlags -decodeFeatureFlags = FeatureFlags <$> getWord64le - -instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag FeatureFlags where - type Tag FeatureFlags = 'FeatureFlagsTag - toMerkleNode = encodeMerkleInputNode encodeFeatureFlags - fromMerkleNode = decodeMerkleInputNode decodeFeatureFlags - -mkFeatureFlags :: FeatureFlags -mkFeatureFlags = FeatureFlags 0x0 - -- -------------------------------------------------------------------------- -- -- Newtype wrappers for function parameters @@ -476,7 +609,6 @@ isGenesisBlockHeader :: BlockHeader -> Bool isGenesisBlockHeader b = _blockHeight b == genesisHeight (_chainwebVersion b) (_chainId b) --- -- | The genesis block hash includes the Chainweb version and the 'ChainId' -- within the Chainweb version. -- @@ -490,7 +622,7 @@ genesisParentBlockHash v p = BlockHash $ MerkleLogHash , encodeMerkleInputNode encodeChainId (_chainId p) ] -{-# noinline genesisBlockHeaderCache #-} +{-# NOINLINE genesisBlockHeaderCache #-} genesisBlockHeaderCache :: IORef (HashMap ChainwebVersionCode (HashMap ChainId BlockHeader)) genesisBlockHeaderCache = unsafePerformIO $ do let mkMainnetHeader = makeGenesisBlockHeader mainnet @@ -608,139 +740,6 @@ genesisGraph v = chainGraphAt v_ . genesisHeight' v_ . _chainId genesisHeight :: HasCallStack => ChainwebVersion -> ChainId -> BlockHeight genesisHeight v c = _blockHeight (genesisBlockHeader v c) --- -------------------------------------------------------------------------- -- --- Block Header - --- | BlockHeader --- --- Values of this type should never be constructed directly by external code. --- Instead the 'newBlockHeader' smart constructor should be used. Once --- constructed 'BlockHeader' values must not be modified. --- --- Some redundant, aggregated information is included in the block and the block --- hash. This enables nodes to check blocks inductively with respect to existing --- blocks without recalculating the aggregated value from the genesis block --- onward. --- --- The POW hash is not included, since it can be derived from the Nonce and the --- other fields of the 'BlockHeader'. --- --- /IMPORTANT/: Fields in this record must have pairwise distinct types. --- -data BlockHeader :: Type where - BlockHeader :: - { _blockFlags :: {-# UNPACK #-} !FeatureFlags - -- ^ An 8-byte bitmask reserved for the future addition of boolean - -- "feature flags". - - , _blockCreationTime :: {-# UNPACK #-} !BlockCreationTime - -- ^ The time when the block was creates as recorded by the miner - -- of the block. The value must be strictly monotonically increasing - -- within the chain of blocks. Nodes must ignore blocks with values - -- that are in the future and reconsider a block when its value is - -- in the past. Nodes do not have to store blocks until they become - -- recent (but may do it). - -- - -- The block creation time is used to determine the block difficulty for - -- future blocks. - -- - -- Nodes are not supposed to consider the creation time when - -- choosing between two valid (this implies that creation time of a - -- block is not the future) forks. - -- - -- This creates an incentive for nodes to maintain an accurate clock - -- with respect to an (unspecified) commonly accepted time source, - -- such as the public NTP network. - -- - -- It is possible that a miner always chooses the smallest possible - -- creation time value. It is not clear what advantage a miner would - -- gain from doing so, but attack models should consider and - -- investigate such behavior. - -- - -- On the other hand miners may choose to compute forks with creation - -- time long in the future. By doing so, the difficulty on such a fork - -- would decrease allowing the miner to compute very long chains very - -- quickly. However, those chains would become valid only after a long - -- time passed and would be of low PoW weight. The algorithm for - -- computing the difficulty must ensure this strategy doesn't give - -- an advantage to an attacker that would increase the success - -- probability for an attack. - - , _blockParent :: {-# UNPACK #-} !BlockHash - -- ^ authoritative - - , _blockAdjacentHashes :: !BlockHashRecord - -- ^ authoritative - - , _blockTarget :: {-# UNPACK #-} !HashTarget - -- ^ authoritative - - , _blockPayloadHash :: {-# UNPACK #-} !BlockPayloadHash - -- ^ authoritative - - , _blockChainId :: {-# UNPACK #-} !ChainId - - , _blockWeight :: {-# UNPACK #-} !BlockWeight - -- ^ the accumulated weight of the chain. It is redundant information - -- that is subject to the inductive property that the block weight - -- of a block is the block weight of the parent plus the difficulty - -- of the block. - - , _blockHeight :: {-# UNPACK #-} !BlockHeight - -- ^ block height records the length of the chain. It is redundant - -- information and thus subject the inductive property that - -- the block height of a block is the block height of its parent - -- plus one. - - , _blockChainwebVersion :: !ChainwebVersion - -- ^ the Chainweb version is a constant for the chain. A chain - -- is uniquely identified by its genesis block. Thus this is - -- redundant information and thus subject to the inductive property - -- that the Chainweb version of a block equals the Chainweb version - -- of its parent. - - , _blockEpochStart :: {-# UNPACK #-} !EpochStartTime - -- ^ The start time of the current difficulty adjustment epoch. - -- Epochs divide the sequence of blocks in the chain into continuous - -- ranges of blocks. Each epoch is defined by the minimal block - -- height of the blocks in the epoch. - - , _blockNonce :: {-# UNPACK #-} !Nonce - -- ^ authoritative - - , _blockHash :: {-# UNPACK #-} !BlockHash - -- ^ the hash of the block. It includes all of the above block properties. - } - -> BlockHeader - deriving (Show, Generic) - deriving anyclass (NFData) - -instance Eq BlockHeader where - (==) = (==) `on` _blockHash - -instance Ord BlockHeader where - compare = compare `on` _blockHash - -instance Hashable BlockHeader where - hashWithSalt s = hashWithSalt s . _blockHash - -instance HasChainId BlockHeader where - _chainId = _blockChainId - -instance HasChainGraph BlockHeader where - _chainGraph h = _chainGraph (_blockChainwebVersion h, _blockHeight h) - -instance HasChainwebVersion BlockHeader where - _chainwebVersion = _blockChainwebVersion - -instance IsCasValue BlockHeader where - type CasKeyType BlockHeader = BlockHash - casKey = _blockHash - -type BlockHeaderCas tbl = Cas tbl BlockHeader - -makeLenses ''BlockHeader - instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader where -- /IMPORTANT/ a types must occur at most once in this list From c08bda2b7d35f9a0f435139d4c39f790dd28baa5 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 11:09:20 -0500 Subject: [PATCH 20/91] fix bench --- bench/Bench.hs | 3 +++ bench/Chainweb/Pact/Backend/ForkingBench.hs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index cafc991a97..8e501f4e06 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -15,9 +15,12 @@ import qualified Chainweb.Pact.Backend.ForkingBench as ForkingBench import qualified JSONEncoding import Chainweb.Storage.Table.RocksDB +import Chainweb.Version.Development +import Chainweb.Version.Registry main :: IO () main = withTempRocksDb "benchmarks" $ \rdb -> do + registerVersion Development defaultMain [ Checkpointer.bench , ForkingBench.bench rdb diff --git a/bench/Chainweb/Pact/Backend/ForkingBench.hs b/bench/Chainweb/Pact/Backend/ForkingBench.hs index 905b2f381f..2ef2aa17f2 100644 --- a/bench/Chainweb/Pact/Backend/ForkingBench.hs +++ b/bench/Chainweb/Pact/Backend/ForkingBench.hs @@ -379,7 +379,7 @@ cid :: ChainId cid = someChainId testVer testVer :: ChainwebVersion -testVer = fastForkingCpmTestVersion petersonChainGraph +testVer = slowForkingCpmTestVersion petersonChainGraph assertNotLeft :: (MonadThrow m, Exception e) => Either e a -> m a assertNotLeft (Left l) = throwM l From d3ce26397f70e0b562178c5ad0663048744a2d45 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 12:30:32 -0500 Subject: [PATCH 21/91] Disable POW by default in devnet, but allow enabling it --- src/Chainweb/Chainweb/Configuration.hs | 5 ++++- src/Chainweb/Version/Development.hs | 4 ++-- src/Chainweb/Version/Registry.hs | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index fe8626c698..6cee8bd9aa 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -569,8 +569,9 @@ pChainwebConfiguration = id <$> optional knownVersion <*> optional (textOption @Fork (long "fork-upper-bound" <> help "(development mode only) the latest fork the node will enable")) <*> optional (BlockRate <$> textOption (long "block-rate" <> help "(development mode only) the block rate in seconds per block")) + <*> switch (long "enable-pow" <> help "(development mode only) enable proof of work checks") where - constructVersion cliVersion fub br oldVersion + constructVersion cliVersion fub br enablePow oldVersion | _versionCode winningVersion == _versionCode devnet = winningVersion { _versionBlockRate = fromMaybe (_versionBlockRate winningVersion) br , _versionForks = @@ -585,6 +586,8 @@ pChainwebConfiguration = id in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) (HS.toMap (chainIds winningVersion)) ) fub + , _versionCheats = + _versionCheats winningVersion & disablePow .~ not enablePow } | Nothing <- br, Nothing <- fub = winningVersion | otherwise = error diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 915ae9e309..0169e3b10d 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -85,7 +85,7 @@ devnet = ChainwebVersion (to20ChainsDevelopment, twentyChainGraph) `Above` End petersonChainGraph - , _versionBlockRate = BlockRate 30_000_000 + , _versionBlockRate = BlockRate 10_000_000 , _versionWindow = Just $ WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionFakeFirstEpochStart = True @@ -106,7 +106,7 @@ devnet = ChainwebVersion , _versionMaxBlockGasLimit = End (Just 180_000) , _versionCheats = Cheats { _disablePeerValidation = True - , _disablePow = False + , _disablePow = True , _disablePact = False , _disableMempool = False } diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index a8b41da08f..b72d1bf8ba 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -75,7 +75,9 @@ lookupVersionByCode code lookupVersion = unsafeDupablePerformIO $ do m <- readIORef versionMap return $ fromMaybe (error notRegistered) $ HM.lookup code m - notRegistered = "version not registered with code " <> show code <> ", have you seen Chainweb.Test.TestVersions.legalizeTestVersion?" + notRegistered + | code == _versionCode devnet = "devnet version used but not registered, remember to do so after it's configured" + | otherwise = "version not registered with code " <> show code <> ", have you seen Chainweb.Test.TestVersions.legalizeTestVersion?" -- | Versions known to us by name. knownVersions :: [ChainwebVersion] From c6792ba1baaa0936610e0b867cbaa9efeb70722e Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 13:04:26 -0500 Subject: [PATCH 22/91] Remove quotes from Show ChainwebVersionName --- src/Chainweb/NodeVersion.hs | 4 ++-- src/Chainweb/Version.hs | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Chainweb/NodeVersion.hs b/src/Chainweb/NodeVersion.hs index b2a28409bb..73a1008b9c 100644 --- a/src/Chainweb/NodeVersion.hs +++ b/src/Chainweb/NodeVersion.hs @@ -137,7 +137,7 @@ req ver addr endpoint = HTTP.defaultRequest , HTTP.path = T.encodeUtf8 $ T.intercalate "/" [ "/chainweb" , prettyApiVersion - , sshow ver + , getChainwebVersionName ver , endpoint ] , HTTP.responseTimeout = HTTP.responseTimeoutMicro requestTimeoutMicros @@ -157,7 +157,7 @@ cutReq ver addr = HTTP.defaultRequest , HTTP.path = T.encodeUtf8 $ T.intercalate "/" [ "/chainweb" , prettyApiVersion - , sshow ver + , getChainwebVersionName ver , "cut" ] , HTTP.responseTimeout = HTTP.responseTimeoutMicro requestTimeoutMicros diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 7bc10ac91c..a3bb10d247 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -272,9 +272,11 @@ instance FromJSONKey Fork where newtype ChainwebVersionName = ChainwebVersionName { getChainwebVersionName :: T.Text } deriving stock (Generic, Eq, Ord) - deriving newtype (Show, ToJSON, FromJSON) + deriving newtype (ToJSON, FromJSON) deriving anyclass (Hashable, NFData) +instance Show ChainwebVersionName where show = T.unpack . getChainwebVersionName + newtype ChainwebVersionCode = ChainwebVersionCode { getChainwebVersionCode :: Word32 } deriving stock (Generic, Eq, Ord) @@ -423,7 +425,7 @@ someChainwebVersionVal :: ChainwebVersion -> SomeChainwebVersionT someChainwebVersionVal v = someChainwebVersionVal' (_versionName v) someChainwebVersionVal' :: ChainwebVersionName -> SomeChainwebVersionT -someChainwebVersionVal' v = case someSymbolVal (sshow v) of +someChainwebVersionVal' v = case someSymbolVal (show v) of (SomeSymbol (Proxy :: Proxy v)) -> SomeChainwebVersionT (Proxy @('ChainwebVersionT v)) -- -- -------------------------------------------------------------------------- -- From 92317613f96743a99df53e0a18b38641a2bd2f86 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 13:12:15 -0500 Subject: [PATCH 23/91] enable pow by default on Development --- src/Chainweb/Chainweb/Configuration.hs | 6 +++--- src/Chainweb/Version/Development.hs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 6cee8bd9aa..2c345cb88b 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -569,9 +569,9 @@ pChainwebConfiguration = id <$> optional knownVersion <*> optional (textOption @Fork (long "fork-upper-bound" <> help "(development mode only) the latest fork the node will enable")) <*> optional (BlockRate <$> textOption (long "block-rate" <> help "(development mode only) the block rate in seconds per block")) - <*> switch (long "enable-pow" <> help "(development mode only) enable proof of work checks") + <*> switch (long "disable-pow" <> help "(development mode only) disable proof of work check") where - constructVersion cliVersion fub br enablePow oldVersion + constructVersion cliVersion fub br disablePow' oldVersion | _versionCode winningVersion == _versionCode devnet = winningVersion { _versionBlockRate = fromMaybe (_versionBlockRate winningVersion) br , _versionForks = @@ -587,7 +587,7 @@ pChainwebConfiguration = id (HS.toMap (chainIds winningVersion)) ) fub , _versionCheats = - _versionCheats winningVersion & disablePow .~ not enablePow + _versionCheats winningVersion & disablePow .~ disablePow' } | Nothing <- br, Nothing <- fub = winningVersion | otherwise = error diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 0169e3b10d..915ae9e309 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -85,7 +85,7 @@ devnet = ChainwebVersion (to20ChainsDevelopment, twentyChainGraph) `Above` End petersonChainGraph - , _versionBlockRate = BlockRate 10_000_000 + , _versionBlockRate = BlockRate 30_000_000 , _versionWindow = Just $ WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionFakeFirstEpochStart = True @@ -106,7 +106,7 @@ devnet = ChainwebVersion , _versionMaxBlockGasLimit = End (Just 180_000) , _versionCheats = Cheats { _disablePeerValidation = True - , _disablePow = True + , _disablePow = False , _disablePact = False , _disableMempool = False } From 63c1bcf6b4ab5034847242e0bfd9adf1e2691e26 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 13:43:45 -0500 Subject: [PATCH 24/91] Revert some guard heights for devnet, add HasCallStack to some places in Difficulty --- src/Chainweb/Difficulty.hs | 6 ++++-- src/Chainweb/Version/Development.hs | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index e8b47fedff..40cba31fb5 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -76,6 +76,7 @@ import Data.Hashable import qualified Data.Text as T import GHC.Generics +import GHC.Stack import GHC.TypeNats import Text.Printf (printf) @@ -279,7 +280,7 @@ decodeHashDifficultyBe = HashDifficulty <$!> decodePowHashNatBe -- | Given the same `ChainwebVersion`, forms an isomorphism with -- `difficultyToTarget`. -- -targetToDifficulty :: HashTarget -> HashDifficulty +targetToDifficulty :: HasCallStack => HashTarget -> HashDifficulty targetToDifficulty (HashTarget (PowHashNat target)) = HashDifficulty . PowHashNat $ maxTargetWord `div` target {-# INLINE targetToDifficulty #-} @@ -330,7 +331,8 @@ adjust (BlockRate br) (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = -- This is used when 'oldDaGuard' is active. -- legacyAdjust - :: BlockRate + :: HasCallStack + => BlockRate -> WindowWidth -> TimeSpan Micros -- ^ the actual time of the last epoch: creation time minus the epoch diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 915ae9e309..0beffee15f 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -41,12 +41,12 @@ devnet = ChainwebVersion , _versionName = ChainwebVersionName "development" , _versionForks = tabulateHashMap $ \case - Vuln797Fix -> AllChains 1 - SlowEpoch -> AllChains 1 - OldTargetGuard -> AllChains 1 + Vuln797Fix -> AllChains 0 + SlowEpoch -> AllChains 0 + OldTargetGuard -> AllChains 0 EnforceKeysetFormats -> AllChains 1 SkipFeatureFlagValidation -> AllChains 1 - OldDAGuard -> AllChains 1 + OldDAGuard -> AllChains 13 CheckTxHash -> AllChains 1 PactEvents -> AllChains 1 SkipTxTimingValidation -> AllChains 1 From 2fad2593f2dcf1995f8958976e2ca5abd1c984f1 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 3 Mar 2023 15:56:17 -0500 Subject: [PATCH 25/91] Make upgrades coinv3-coinv5 precocious on devnet --- src/Chainweb/Version/Development.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 915ae9e309..1a8115f845 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -74,9 +74,9 @@ devnet = ChainwebVersion [ [(unsafeChainId 0, upgrade Devnet.transactions)] , [(unsafeChainId i, upgrade Devnet.transactions) | i <- [1..9]] ]) - , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) - , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) - , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) + , (Pact4Coin3, AllChains (Upgrade CoinV3.transactions True)) + , (Chainweb214Pact, AllChains (Upgrade CoinV4.transactions True)) + , (Chainweb215Pact, AllChains (Upgrade CoinV5.transactions True)) ] , onChains [(unsafeChainId 0, HM.singleton to20ChainsDevelopment (upgrade MNKAD.transactions))] ] From 6c51702877adec3051f7e0a266ac50bcfe4eca20 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 15:42:45 -0500 Subject: [PATCH 26/91] More cleanup --- chainweb.cabal | 2 - src/Chainweb/BlockHeader.hs | 4 +- src/Chainweb/Chainweb/CheckReachability.hs | 4 +- src/Chainweb/Chainweb/PeerResources.hs | 7 - src/Chainweb/Miner/Miners.hs | 2 +- src/Chainweb/Miner/Pact.hs | 2 +- .../Pact/Transactions/UpgradeTransactions.hs | 93 --------- src/Chainweb/Pact/Utils.hs | 16 +- src/Chainweb/Payload.hs | 1 - src/Chainweb/Payload/PayloadStore/InMemory.hs | 1 + src/Chainweb/Version.hs | 71 +++---- src/Chainweb/Version/Utils.hs | 14 +- src/Chainweb/WebPactExecutionService.hs | 2 +- src/Control/Concurrent/FixedThreadPool.hs | 182 ------------------ test/Chainweb/Test/Orphans/External.hs | 34 ---- .../Chainweb/Test/Pact/PactSingleChainTest.hs | 1 + test/Chainweb/Test/TestVersions.hs | 9 +- 17 files changed, 57 insertions(+), 388 deletions(-) delete mode 100644 src/Chainweb/Pact/Transactions/UpgradeTransactions.hs delete mode 100644 src/Control/Concurrent/FixedThreadPool.hs delete mode 100644 test/Chainweb/Test/Orphans/External.hs diff --git a/chainweb.cabal b/chainweb.cabal index a863feff96..808697f256 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -245,8 +245,6 @@ library , Chainweb.WebBlockHeaderDB , Chainweb.WebPactExecutionService - , Control.Concurrent.FixedThreadPool - , Data.IVar , Data.LogMessage , Data.PQueue diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index f0d5cde14b..10dd42351e 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -415,8 +415,8 @@ slowEpoch :: ParentHeader -> BlockCreationTime -> Bool slowEpoch (ParentHeader p) (BlockCreationTime ct) = actual > (expected * 5) where EpochStartTime es = _blockEpochStart p - BlockRate s = blockRate (_blockChainwebVersion p) - WindowWidth ww = fromJuste $ window (_blockChainwebVersion p) + BlockRate s = _versionBlockRate (_blockChainwebVersion p) + WindowWidth ww = fromJuste $ _versionWindow (_blockChainwebVersion p) expected :: Micros expected = s * int ww diff --git a/src/Chainweb/Chainweb/CheckReachability.hs b/src/Chainweb/Chainweb/CheckReachability.hs index 6ddd9e312a..34ba78356e 100644 --- a/src/Chainweb/Chainweb/CheckReachability.hs +++ b/src/Chainweb/Chainweb/CheckReachability.hs @@ -15,6 +15,7 @@ module Chainweb.Chainweb.CheckReachability ( ReachabilityException(..) , checkReachability +, peerServerSettings ) where import Configuration.Utils hiding (Error, Lens') @@ -144,9 +145,6 @@ checkReachability sock mgr v logger pdb peers peer threshold = do loggServerError (Just r) e = "HTTP server error: " <> sshow e <> ". Request: " <> sshow r loggServerError Nothing e = "HTTP server error: " <> sshow e --- TODO move that elsewhere and unify with method from --- Chainweb.Chainweb.PeerResources --- peerServerSettings :: Peer -> W.Settings peerServerSettings peer = W.setPort (int . _hostAddressPort . _peerAddr $ _peerInfo peer) diff --git a/src/Chainweb/Chainweb/PeerResources.hs b/src/Chainweb/Chainweb/PeerResources.hs index b269263bdd..b8d1e132db 100644 --- a/src/Chainweb/Chainweb/PeerResources.hs +++ b/src/Chainweb/Chainweb/PeerResources.hs @@ -60,7 +60,6 @@ import GHC.Generics import qualified Network.HTTP.Client as HTTP import Network.Socket (Socket) -import Network.Wai.Handler.Warp (Settings, defaultSettings, setHost, setPort) import Prelude hiding (log) @@ -154,12 +153,6 @@ withPeerResources v conf logger inner = withPeerSocket conf $ \(conf', sock) -> inner logger' (PeerResources conf'' peer sock localDb mgr logger') -peerServerSettings :: Peer -> Settings -peerServerSettings peer - = setPort (int . _hostAddressPort . _peerAddr $ _peerInfo peer) - . setHost (_peerInterface peer) - $ defaultSettings - -- | Setup the local hostname. -- -- If the configured hostname is "0.0.0.0" (i.e. 'anyIpv4'), the hostname is diff --git a/src/Chainweb/Miner/Miners.hs b/src/Chainweb/Miner/Miners.hs index 0a1bd183a1..3033d5d5f5 100644 --- a/src/Chainweb/Miner/Miners.hs +++ b/src/Chainweb/Miner/Miners.hs @@ -101,7 +101,7 @@ localTest lf v coord m cdb gen miners = void $ awaitNewCut cdb c where meanBlockTime :: Double - meanBlockTime = int (_getBlockRate (blockRate v)) / 1_000_000 + meanBlockTime = int (_getBlockRate (_versionBlockRate v)) / 1_000_000 go :: BlockHeight -> WorkHeader -> IO SolvedWork go height w = do diff --git a/src/Chainweb/Miner/Pact.hs b/src/Chainweb/Miner/Pact.hs index d482490148..11208d8cb8 100644 --- a/src/Chainweb/Miner/Pact.hs +++ b/src/Chainweb/Miner/Pact.hs @@ -58,7 +58,7 @@ import Data.Word -- internal modules import Chainweb.BlockHeight (BlockHeight(..)) -import Chainweb.Payload (MinerData(..)) +import Chainweb.Payload import Chainweb.Utils import Pact.Types.Term (KeySet(..), mkKeySet) diff --git a/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs b/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs deleted file mode 100644 index cdfc747741..0000000000 --- a/src/Chainweb/Pact/Transactions/UpgradeTransactions.hs +++ /dev/null @@ -1,93 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeApplications #-} -module Chainweb.Pact.Transactions.UpgradeTransactions -( upgradeTransactions -, twentyChainUpgradeTransactions -, coinV3Transactions -, coinV4Transactions -, coinV5Transactions -) where - -import Chainweb.Version -import Chainweb.Transaction -import Chainweb.Pact.Service.Types -import Chainweb.Utils - -import qualified Chainweb.Pact.Transactions.Mainnet0Transactions as MN0 -import qualified Chainweb.Pact.Transactions.Mainnet1Transactions as MN1 -import qualified Chainweb.Pact.Transactions.Mainnet2Transactions as MN2 -import qualified Chainweb.Pact.Transactions.Mainnet3Transactions as MN3 -import qualified Chainweb.Pact.Transactions.Mainnet4Transactions as MN4 -import qualified Chainweb.Pact.Transactions.Mainnet5Transactions as MN5 -import qualified Chainweb.Pact.Transactions.Mainnet6Transactions as MN6 -import qualified Chainweb.Pact.Transactions.Mainnet7Transactions as MN7 -import qualified Chainweb.Pact.Transactions.Mainnet8Transactions as MN8 -import qualified Chainweb.Pact.Transactions.Mainnet9Transactions as MN9 -import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD -import qualified Chainweb.Pact.Transactions.DevelopmentTransactions as Devnet -import qualified Chainweb.Pact.Transactions.OtherTransactions as Other -import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 -import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 -import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 - -upgradeTransactions :: ChainwebVersion dc -> ChainId -> IO [ChainwebTransaction] -upgradeTransactions Mainnet01 cid = case cidInt of - 0 -> MN0.transactions - 1 -> MN1.transactions - 2 -> MN2.transactions - 3 -> MN3.transactions - 4 -> MN4.transactions - 5 -> MN5.transactions - 6 -> MN6.transactions - 7 -> MN7.transactions - 8 -> MN8.transactions - 9 -> MN9.transactions - c | c >= 10, c <= 19 -> return [] - c -> internalError $ "Invalid mainnet chain id: " <> sshow c - where cidInt :: Int - cidInt = chainIdInt cid -upgradeTransactions Development{} cid = case chainIdInt @Int cid of - c | c >= 0, c <= 9 -> Devnet.transactions - c | c >= 10, c <= 19 -> return [] - c -> internalError $ "Invalid devnet chain id: " <> sshow c -upgradeTransactions _ _ = Other.transactions - -twentyChainUpgradeTransactions :: ChainwebVersion dc -> ChainId -> IO [ChainwebTransaction] -twentyChainUpgradeTransactions Mainnet01 cid = case chainIdInt @Int cid of - 0 -> MNKAD.transactions - c | c >= 1, c <= 19 -> return [] - c -> internalError $ "Invalid mainnet chain id: " <> sshow c -twentyChainUpgradeTransactions Development{} cid = case chainIdInt @Int cid of - 0 -> MNKAD.transactions -- just remeds - c | c >= 1, c <= 19 -> return [] - c -> internalError $ "Invalid devnet chain id: " <> sshow c -twentyChainUpgradeTransactions (FastTimedCPM _) cid = case chainIdInt @Int cid of - c | c == 0, c == 1, c == 2 -> return [] - {-- NOTE: Remediations occur in Chain 3 instead of Chain 0 for this version. - This allows for testing that Rosetta correctly handles remediation - txs without breaking the SPV tests. --} - 3 -> MNKAD.transactions -- just remeds - c | c <= 19 -> return [] - c -> internalError $ "Invalid fasttimecpm chain id: " <> sshow c -twentyChainUpgradeTransactions _ _ = return [] - -coinV3Transactions :: IO [ChainwebTransaction] -coinV3Transactions = CoinV3.transactions - -coinV4Transactions :: IO [ChainwebTransaction] -coinV4Transactions = CoinV4.transactions - -coinV5Transactions :: IO [ChainwebTransaction] -coinV5Transactions = CoinV5.transactions - --- NOTE (linda): When adding new forking transactions that are injected --- into a block's coinbase transaction, please add a corresponding case --- in Rosetta's `matchLogs` (Chainweb.Rosetta.Internal.hs) function and --- follow the coinv3 pattern. --- --- Otherwise, Rosetta tooling has no idea that these upgrade transactions --- occurred. --- This is especially important if the transaction changes an account's balance. --- Rosetta tooling will error out if an account's balance changed and it --- didn't see the transaction that caused the change. --- diff --git a/src/Chainweb/Pact/Utils.hs b/src/Chainweb/Pact/Utils.hs index d01e81f8c2..15035742ce 100644 --- a/src/Chainweb/Pact/Utils.hs +++ b/src/Chainweb/Pact/Utils.hs @@ -24,6 +24,9 @@ module Chainweb.Pact.Utils , pubKeyToKAccountKeySet , generateKeySetFromKAccount , validateKAccountKeySet + + -- * empty payload + , emptyPayload ) where import Data.Aeson @@ -40,8 +43,10 @@ import Pact.Types.KeySet (validateKeyFormat) -- Internal modules import Chainweb.ChainId +import Chainweb.Miner.Pact +import Chainweb.Payload import Chainweb.Time - +import Chainweb.Utils fromPactChainId :: MonadThrow m => P.ChainId -> m ChainId fromPactChainId (P.ChainId t) = chainIdFromText t @@ -99,3 +104,12 @@ validateKAccountKeySet kacct actualKeySet = Just expectedKeySet | expectedKeySet == actualKeySet -> True | otherwise -> False + +-- | Empty payload marking no-op transaction payloads. +-- +emptyPayload :: PayloadWithOutputs +emptyPayload = PayloadWithOutputs mempty miner coinbase h i o + where + BlockPayload h i o = newBlockPayload miner coinbase mempty + miner = MinerData $ encodeToByteString noMiner + coinbase = noCoinbaseOutput \ No newline at end of file diff --git a/src/Chainweb/Payload.hs b/src/Chainweb/Payload.hs index 3153c8ce31..77e75df21b 100644 --- a/src/Chainweb/Payload.hs +++ b/src/Chainweb/Payload.hs @@ -1130,4 +1130,3 @@ verifyPayloadWithOutputs p (_payloadWithOutputsMiner p) (_payloadWithOutputsCoinbase p) (_payloadWithOutputsTransactions p) - diff --git a/src/Chainweb/Payload/PayloadStore/InMemory.hs b/src/Chainweb/Payload/PayloadStore/InMemory.hs index f8b072d112..83af65f7db 100644 --- a/src/Chainweb/Payload/PayloadStore/InMemory.hs +++ b/src/Chainweb/Payload/PayloadStore/InMemory.hs @@ -9,6 +9,7 @@ -- -- An in-memory block payload store. -- +-- TODO: move to tests module Chainweb.Payload.PayloadStore.InMemory ( newPayloadDb diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index a3bb10d247..d84e86a306 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -39,7 +39,9 @@ -- between all nodes running on the same network. -- module Chainweb.Version - ( Fork(..) + ( + -- * Properties of Chainweb Version + Fork(..) , ChainwebGenesis(..) , Cheats(..) , disableMempool @@ -47,6 +49,8 @@ module Chainweb.Version , disablePeerValidation , disablePow , ChainwebVersionCode(..) + , encodeChainwebVersionCode + , decodeChainwebVersionCode , ChainwebVersionName(..) , ChainwebVersion(..) , Upgrade(..) @@ -63,25 +67,11 @@ module Chainweb.Version , versionMaxBlockGasLimit , versionName , versionWindow - , window - , blockRate , versionGenesis , genesisBlockPayload , genesisBlockPayloadHash , genesisBlockTarget , genesisTime - , emptyPayload - , forkUpgrades - , latestBehaviorAt - , domainAddr2PeerInfo - , encodeChainwebVersionCode - , decodeChainwebVersionCode - - -- * Properties of Chainweb Version - -- ** POW - , BlockRate(..) - , WindowWidth(..) - -- ** Payload Validation Parameters -- * Typelevel ChainwebVersion , ChainwebVersionT(..) @@ -128,6 +118,11 @@ module Chainweb.Version , adjsOfVertex , checkAdjacentChainIds + -- ** Utilities for constructing Chainweb Version + , forkUpgrades + , latestBehaviorAt + , domainAddr2PeerInfo + -- * Internal. Don't use. Exported only for testing -- , headerSizes -- , headerBaseSizeBytes @@ -164,7 +159,6 @@ import Chainweb.Difficulty import Chainweb.Graph import Chainweb.HostAddress import Chainweb.MerkleUniverse -import Chainweb.Miner.Pact import Chainweb.Payload import Chainweb.Transaction import Chainweb.Utils @@ -183,6 +177,7 @@ import P2P.Peer domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] domainAddr2PeerInfo = fmap (PeerInfo Nothing) +-- edtodo properly order by original mainnet height data Fork = Vuln797Fix | SlowEpoch @@ -205,8 +200,8 @@ data Fork | Chainweb215Pact | Chainweb216Pact | Chainweb217Pact - | Chainweb218Pact | Pact44NewTrans + | Chainweb218Pact -- always add new forks at the end, not in the middle of the constructors. deriving (Bounded, Generic, NFData, Hashable, Eq, Enum, Ord, Show) @@ -283,6 +278,17 @@ newtype ChainwebVersionCode = deriving newtype (Show, ToJSON, FromJSON) deriving anyclass (Hashable, NFData) +encodeChainwebVersionCode :: ChainwebVersionCode -> Put +encodeChainwebVersionCode = putWord32le . getChainwebVersionCode + +decodeChainwebVersionCode :: Get ChainwebVersionCode +decodeChainwebVersionCode = ChainwebVersionCode <$> getWord32le + +instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVersionCode where + type Tag ChainwebVersionCode = 'ChainwebVersionTag + toMerkleNode = encodeMerkleInputNode encodeChainwebVersionCode + fromMerkleNode = decodeMerkleInputNode decodeChainwebVersionCode + data Upgrade = Upgrade { _upgradeTransactions :: [ChainwebTransaction] , _legacyUpgradeIsPrecocious :: Bool @@ -322,13 +328,7 @@ data ChainwebVersion deriving anyclass NFData instance Show ChainwebVersion where - show = T.unpack . getChainwebVersionName . _versionName - -window :: ChainwebVersion -> Maybe WindowWidth -window = _versionWindow - -blockRate :: ChainwebVersion -> BlockRate -blockRate = _versionBlockRate + show = show . _versionName instance Ord ChainwebVersion where v `compare` v' = fold @@ -344,6 +344,8 @@ instance Ord ChainwebVersion where , _versionMaxBlockGasLimit v `compare` _versionMaxBlockGasLimit v' , _versionFakeFirstEpochStart v `compare` _versionFakeFirstEpochStart v' , _versionBootstraps v `compare` _versionBootstraps v' + -- genesis cannot be ordered because Payload in Pact cannot be ordered + -- , _versionGenesis v `compare` _versionGenesis v' , _versionCheats v `compare` _versionCheats v' ] @@ -380,27 +382,6 @@ makeLensesWith (lensRules & generateLazyPatterns .~ True) 'Cheats genesisBlockPayloadHash :: ChainwebVersion -> ChainId -> BlockPayloadHash genesisBlockPayloadHash v cid = v ^?! versionGenesis . genesisBlockPayload . onChain cid . to _payloadWithOutputsPayloadHash --- | Empty payload marking no-op transaction payloads for deprecated --- versions. --- -emptyPayload :: PayloadWithOutputs -emptyPayload = PayloadWithOutputs mempty miner coinbase h i o - where - BlockPayload h i o = newBlockPayload miner coinbase mempty - miner = MinerData $ encodeToByteString noMiner - coinbase = noCoinbaseOutput - -encodeChainwebVersionCode :: ChainwebVersionCode -> Put -encodeChainwebVersionCode = putWord32le . getChainwebVersionCode - -decodeChainwebVersionCode :: Get ChainwebVersionCode -decodeChainwebVersionCode = ChainwebVersionCode <$> getWord32le - -instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVersionCode where - type Tag ChainwebVersionCode = 'ChainwebVersionTag - toMerkleNode = encodeMerkleInputNode encodeChainwebVersionCode - fromMerkleNode = decodeMerkleInputNode decodeChainwebVersionCode - instance HasTextRepresentation ChainwebVersionName where toText = getChainwebVersionName fromText = pure . ChainwebVersionName diff --git a/src/Chainweb/Version/Utils.hs b/src/Chainweb/Version/Utils.hs index 2682049e60..066cf84d4f 100644 --- a/src/Chainweb/Version/Utils.hs +++ b/src/Chainweb/Version/Utils.hs @@ -267,7 +267,7 @@ globalBlockRateAt -> Double globalBlockRateAt v h = (int r / 1_000_000) / int (chainCountAt v h) where - BlockRate r = blockRate (_chainwebVersion v) + BlockRate r = _versionBlockRate (_chainwebVersion v) -- -------------------------------------------------------------------------- -- -- Cut Heights @@ -306,7 +306,6 @@ chainGraphsByCutHeight = M.fromList -- chainGraphAtCutHeight :: HasChainwebVersion v => v -> CutHeight -> ChainGraph chainGraphAtCutHeight v h = atCutHeight h $ chainGraphsByCutHeight v -{-# INLINE chainGraphAtCutHeight #-} -- | The number of chains that exist at the given cut height -- @@ -314,7 +313,6 @@ chainGraphAtCutHeight v h = atCutHeight h $ chainGraphsByCutHeight v -- chainCountAtCutHeight :: HasChainwebVersion v => v -> CutHeight -> Natural chainCountAtCutHeight v = order . chainGraphAtCutHeight v -{-# INLINE chainCountAtCutHeight #-} -- | The diameter of the chain graph at the given cut height -- @@ -322,7 +320,6 @@ chainCountAtCutHeight v = order . chainGraphAtCutHeight v -- diameterAtCutHeight :: HasChainwebVersion v => v -> CutHeight -> Natural diameterAtCutHeight v = diameter . chainGraphAtCutHeight v -{-# INLINE diameterAtCutHeight #-} -- | The degree of the chain graph at the given cut height -- @@ -330,7 +327,6 @@ diameterAtCutHeight v = diameter . chainGraphAtCutHeight v -- degreeAtCutHeight :: HasChainwebVersion v => v -> CutHeight -> Natural degreeAtCutHeight v = degree . chainGraphAtCutHeight v -{-# INLINE degreeAtCutHeight #-} -- | The average chain height at a given cut height. -- @@ -339,7 +335,6 @@ degreeAtCutHeight v = degree . chainGraphAtCutHeight v -- avgBlockHeightAtCutHeight :: HasChainwebVersion v => v -> CutHeight -> Double avgBlockHeightAtCutHeight v h = int h / int (chainCountAtCutHeight v h) -{-# INLINE avgBlockHeightAtCutHeight #-} -- | The global number of blocks that exist at the given cut height. -- @@ -356,7 +351,6 @@ blockCountAtCutHeight v h = globalBlockCountAt v (int k `div` int (order g)) + int (h - k) where (k, g) = fromJuste $ M.lookupLE h $ chainGraphsByCutHeight v -{-# INLINE blockCountAtCutHeight #-} lastGraphChangeByCutHeight :: HasCallStack @@ -366,7 +360,6 @@ lastGraphChangeByCutHeight -> CutHeight lastGraphChangeByCutHeight v h = fst $ fromJuste $ M.lookupLE h $ chainGraphsByCutHeight v -{-# INLINE lastGraphChangeByCutHeight #-} nextGraphChangeByCutHeight :: HasCallStack @@ -376,7 +369,6 @@ nextGraphChangeByCutHeight -> CutHeight nextGraphChangeByCutHeight v h = fst $ fromJuste $ M.lookupGT h $ chainGraphsByCutHeight v -{-# INLINE nextGraphChangeByCutHeight #-} -- -------------------------------------------------------------------------- -- -- Expected Block Count, Block Heights, and Cut Heights @@ -396,7 +388,7 @@ expectedBlockCountAfterSeconds v cid s = max 0 (1 + (int s / (int r / 1_000_000) -- The `max 0` term is required for chains that were added during graph transitions -- and thus have `genesisHeight > 0` where - BlockRate r = blockRate (_chainwebVersion v) + BlockRate r = _versionBlockRate (_chainwebVersion v) gh = genesisHeight (_chainwebVersion v) (_chainId cid) -- | This function is useful for performance testing when calculating the @@ -432,7 +424,7 @@ expectedBlockHeightAfterSeconds -> Double expectedBlockHeightAfterSeconds v s = int s / (int r / 1_000_000) where - BlockRate r = blockRate (_chainwebVersion v) + BlockRate r = _versionBlockRate (_chainwebVersion v) -- | The expected CutHeight after the given number of seconds has passed. -- diff --git a/src/Chainweb/WebPactExecutionService.hs b/src/Chainweb/WebPactExecutionService.hs index 5f8ad013c7..ca24378aa0 100644 --- a/src/Chainweb/WebPactExecutionService.hs +++ b/src/Chainweb/WebPactExecutionService.hs @@ -33,9 +33,9 @@ import Chainweb.Miner.Pact import Chainweb.Pact.Service.BlockValidation import Chainweb.Pact.Service.PactQueue import Chainweb.Pact.Service.Types +import Chainweb.Pact.Utils import Chainweb.Payload import Chainweb.Transaction -import Chainweb.Version import Chainweb.Utils (T2) import Pact.Types.Hash diff --git a/src/Control/Concurrent/FixedThreadPool.hs b/src/Control/Concurrent/FixedThreadPool.hs deleted file mode 100644 index dbe1d2a09e..0000000000 --- a/src/Control/Concurrent/FixedThreadPool.hs +++ /dev/null @@ -1,182 +0,0 @@ -{-# LANGUAGE BangPatterns #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE ScopedTypeVariables #-} - -module Control.Concurrent.FixedThreadPool - ( ThreadPool - , Action - , newThreadPool - , stopThreadPool - , killThreadPool - , waitThreadPool - , withThreadPool - , runAction - , runActionGroup - , mapAction - , mapAction_ - , waitAction - ) where - ------------------------------------------------------------------------------- -import Control.Concurrent (ThreadId, forkIOWithUnmask, killThread) -import Control.Concurrent.MVar -import Control.Concurrent.STM -import Control.Concurrent.STM.TBMChan (TBMChan) -import qualified Control.Concurrent.STM.TBMChan as TBMChan -import Control.Exception -import Control.Monad (forever, void, when) -import Data.IORef -import Data.Maybe (isJust) -import Data.Vector (Vector) -import qualified Data.Vector as V ------------------------------------------------------------------------------- - -data Action = Action { - _action :: IO () - , _actionDone :: Maybe SomeException -> IO () - , _wait :: IO (Maybe SomeException) - } - -type Chan = TBMChan Action - -data ThreadPool = ThreadPool { - _threads :: {-# UNPACK #-} !(Vector ThreadId) - , _joins :: {-# UNPACK #-} !(Vector (IO ())) - , _chan :: {-# UNPACK #-} !Chan - } - - ------------------------------------------------------------------------------- -newThreadPool :: Int -> IO ThreadPool -newThreadPool n = do - chan <- atomically $ TBMChan.newTBMChan n - mvars <- V.replicateM n newEmptyMVar - threads <- V.mapM (\m -> forkIOWithUnmask $ \u -> worker chan m u) mvars - let joins = V.map takeMVar mvars - return $! ThreadPool threads joins chan - - where - eatExceptions = handle $ \(e :: SomeException) -> void $ evaluate e - performAction (Action action done _) = - (action >> done Nothing) `catch` (eatExceptions . done . Just) - worker chan mv restore = (`finally` putMVar mv ()) $ eatExceptions $ - restore $ forever $ do - m <- atomically $ TBMChan.readTBMChan chan - case m of - Nothing -> throwIO ThreadKilled - (Just act) -> performAction act - - --- | Runs an IO action asynchronously. Returns an 'Action' you can wait on with --- 'waitAction'. -runAction :: ThreadPool -> IO () -> IO Action -runAction (ThreadPool _ _ chan) action = do - mv <- newEmptyMVar - let actData = Action action (putMVar mv) (readMVar mv) - atomically $ TBMChan.writeTBMChan chan actData - return actData - - -waitAction :: Action -> IO (Maybe SomeException) -waitAction (Action _ _ wait) = wait - - --- | Run a group of actions and wait for them all to finish -data Group = Group { - _sema :: MVar Int - , _groupErr :: IORef (Maybe SomeException) - , _groupDone :: MVar (Maybe SomeException) - } - - -newGroup :: Int -> IO Group -newGroup k = Group <$> newMVar k <*> newIORef Nothing <*> newEmptyMVar - - -doneGroup :: Group -> Maybe SomeException -> IO () -doneGroup (Group sema gerr done) mberr = mask_ $ do - b <- modifyMVar sema dec - when (isJust mberr) handleErr - when b $ void (readIORef gerr >>= tryPutMVar done) - where - handleErr = writeIORef gerr mberr - dec !k = let !k' = k - 1 - in return (k', k' == 0) - - -waitGroup :: Group -> IO (Maybe SomeException) -waitGroup (Group _ _ done) = readMVar done - - --- | Runs a group of IO actions and waits for them to finish. If any of the IO --- actions throws an exception, 'runActionGroup' will return it. If more than --- one action throws an exception, 'runActionGroup' will arbitrarily return one --- of the exceptions. The 'runActionGroup' function will wait until all of the --- actions have run, even if an exception is thrown. -runActionGroup :: Traversable t - => ThreadPool - -> t (IO ()) - -> IO (Maybe SomeException) -runActionGroup (ThreadPool _ _ chan) actions = - if len == 0 then return Nothing else go - where - len = length actions - - go = do - g <- newGroup len - let actData = fmap (toAction g) actions - mapM_ (atomically . TBMChan.writeTBMChan chan) actData - waitGroup g - - toAction g a = Action a (doneGroup g) (waitGroup g) - - -stopThreadPool :: ThreadPool -> IO () -stopThreadPool (ThreadPool _ _ chan) = - atomically $ TBMChan.closeTBMChan chan - - -killThreadPool :: ThreadPool -> IO () -killThreadPool tp@(ThreadPool threads _ _) = do - stopThreadPool tp - V.mapM_ killThread threads - - -waitThreadPool :: ThreadPool -> IO () -waitThreadPool (ThreadPool _ waits _) = V.sequence_ waits - - -withThreadPool :: Int -> (ThreadPool -> IO a) -> IO a -withThreadPool k userFunc = bracket create destroy userFunc - where - create = newThreadPool k - destroy tp = stopThreadPool tp >> waitThreadPool tp - - -mapAction :: Traversable t - => ThreadPool - -> (a -> IO b) - -> t a - -> IO (t (Either SomeException b)) -mapAction tp userFunc xs = do - vals <- mapM zipMV xs - let mvs = fmap snd vals - let actions = fmap toAction vals - mapM_ (runAction tp) actions - mapM takeMVar mvs - - where - zipMV x = do - mv <- newEmptyMVar - return (x, mv) - toAction (x, mv) = try (userFunc x) >>= putMVar mv - - -mapAction_ :: Traversable t - => ThreadPool - -> (a -> IO b) - -> t a - -> IO () -mapAction_ tp userFunc xs0 = do - e <- sequence_ <$> mapAction tp (void . userFunc) xs0 - either throwIO return e diff --git a/test/Chainweb/Test/Orphans/External.hs b/test/Chainweb/Test/Orphans/External.hs deleted file mode 100644 index 6506d32719..0000000000 --- a/test/Chainweb/Test/Orphans/External.hs +++ /dev/null @@ -1,34 +0,0 @@ -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - --- | --- Module: Chainweb.Test.Orphans.External --- Copyright: Copyright © 2018 Kadena LLC. --- License: MIT --- Maintainer: Lars Kuhtz --- Stability: experimental --- --- Orphan instances for types that aren't defined in the chainweb package --- -module Chainweb.Test.Orphans.External -( -) where - -import Data.DoubleWord (Word256) -import Data.Word - -import Network.Socket - -import Test.QuickCheck - -instance Arbitrary Word256 where - arbitrary = arbitrarySizedBoundedIntegral - shrink = shrinkIntegral - -instance Arbitrary SockAddr where - arbitrary = oneof - [ SockAddrInet <$> (fromIntegral @Word16 <$> arbitrary) <*> arbitrary - , SockAddrInet6 <$> (fromIntegral @Word16 <$> arbitrary) <*> arbitrary <*> arbitrary <*> arbitrary - , SockAddrUnix <$> arbitrary - ] diff --git a/test/Chainweb/Test/Pact/PactSingleChainTest.hs b/test/Chainweb/Test/Pact/PactSingleChainTest.hs index 9b049d473b..a35eea40b6 100644 --- a/test/Chainweb/Test/Pact/PactSingleChainTest.hs +++ b/test/Chainweb/Test/Pact/PactSingleChainTest.hs @@ -61,6 +61,7 @@ import Chainweb.Pact.Service.PactQueue (PactQueue) import Chainweb.Pact.Service.Types import Chainweb.Pact.PactService.ExecBlock import Chainweb.Pact.Types +import Chainweb.Pact.Utils (emptyPayload) import Chainweb.Payload import Chainweb.Test.Cut.TestBlockDb import Chainweb.Test.Pact.Utils diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 779d3075bb..a14e53e548 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -32,6 +32,7 @@ import Chainweb.BlockHeight import Chainweb.Difficulty import Chainweb.Graph import Chainweb.HostAddress +import Chainweb.Pact.Utils import Chainweb.Time import Chainweb.Utils import Chainweb.Utils.Rule @@ -101,7 +102,7 @@ testVersionTemplate v = v & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + 0x80000000) & versionHeaderBaseSizeBytes .~ 318 - 110 & versionWindow .~ Nothing - & versionFakeFirstEpochStart .~ False -- DA is already disabled with window = Nothing + & versionFakeFirstEpochStart .~ False -- DA is already disabled with _versionWindow = Nothing & versionMaxBlockGasLimit .~ End (Just 2_000_000) & versionBootstraps .~ [testBootstrapPeerInfos] @@ -143,7 +144,7 @@ barebonesTestVersion' g v = & versionName .~ ChainwebVersionName ("test-" <> toText g) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo , _disablePact = True , _disableMempool = True , _disablePeerValidation = True @@ -167,7 +168,7 @@ cpmTestVersion g v = v & versionBlockRate .~ BlockRate (Micros 100_000) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo , _disablePact = False , _disableMempool = False , _disablePeerValidation = True @@ -261,7 +262,7 @@ timedConsensusVersion' g1 g2 v = & versionWindow .~ Nothing & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with window = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo , _disablePact = True , _disableMempool = True , _disablePeerValidation = True From 479c19ea8fdc6531c4156171223138b8561c20f2 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 16:40:20 -0500 Subject: [PATCH 27/91] delete stm-chans dep --- chainweb.cabal | 1 - 1 file changed, 1 deletion(-) diff --git a/chainweb.cabal b/chainweb.cabal index 808697f256..770be42192 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -387,7 +387,6 @@ library , servant-client-core >= 0.18.2 , servant-server >= 0.18.2 , stm >= 2.4 - , stm-chans , stopwatch >= 0.1 , streaming >= 0.2 , streaming-commons >= 0.2 From edc25e5b614869b25c6a4d0e3bb72022243a7acd Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 18:00:45 -0500 Subject: [PATCH 28/91] Reorder forks according to order of mainnet appearance --- src/Chainweb/Version.hs | 57 ++++++++++++++--------------- src/Chainweb/Version/Development.hs | 26 ++++++------- src/Chainweb/Version/Mainnet.hs | 33 ++++++++--------- src/Chainweb/Version/Testnet.hs | 23 ++++++------ test/Chainweb/Test/TestVersions.hs | 49 ++++++++++++------------- 5 files changed, 92 insertions(+), 96 deletions(-) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index d84e86a306..62091e117f 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -177,82 +177,81 @@ import P2P.Peer domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] domainAddr2PeerInfo = fmap (PeerInfo Nothing) --- edtodo properly order by original mainnet height data Fork - = Vuln797Fix - | SlowEpoch + = SlowEpoch + | Vuln797Fix + | CoinV2 + | PactBackCompat_v16 + | ModuleNameFix + | SkipTxTimingValidation | OldTargetGuard - | EnforceKeysetFormats | SkipFeatureFlagValidation + | ModuleNameFix2 | OldDAGuard - | CheckTxHash | PactEvents - | SkipTxTimingValidation | SPVBridge - | ModuleNameFix - | ModuleNameFix2 - | PactBackCompat_v16 - | CoinV2 | Pact4Coin3 + | EnforceKeysetFormats | Pact420 + | CheckTxHash | Chainweb213Pact | Chainweb214Pact | Chainweb215Pact + | Pact44NewTrans | Chainweb216Pact | Chainweb217Pact - | Pact44NewTrans | Chainweb218Pact -- always add new forks at the end, not in the middle of the constructors. deriving (Bounded, Generic, NFData, Hashable, Eq, Enum, Ord, Show) instance HasTextRepresentation Fork where + toText SlowEpoch = "slowEpoch" toText Vuln797Fix = "vuln797Fix" toText CoinV2 = "coinV2" - toText SlowEpoch = "slowEpoch" + toText PactBackCompat_v16 = "pactBackCompat_v16" + toText ModuleNameFix = "moduleNameFix" + toText SkipTxTimingValidation = "skipTxTimingValidation" toText OldTargetGuard = "oldTargetGuard" - toText EnforceKeysetFormats = "enforceKeysetFormats" toText SkipFeatureFlagValidation = "skipFeatureFlagValidation" + toText ModuleNameFix2 = "moduleNameFix2" toText OldDAGuard = "oldDaGuard" - toText CheckTxHash = "checkTxHash" - toText Pact4Coin3 = "pact4Coin3" toText PactEvents = "pactEvents" - toText SkipTxTimingValidation = "skipTxTimingValidation" toText SPVBridge = "spvBridge" - toText PactBackCompat_v16 = "pactBackCompat_v16" - toText ModuleNameFix = "moduleNameFix" - toText ModuleNameFix2 = "moduleNameFix2" + toText Pact4Coin3 = "pact4Coin3" + toText EnforceKeysetFormats = "enforceKeysetFormats" toText Pact420 = "pact420" + toText CheckTxHash = "checkTxHash" toText Chainweb213Pact = "chainweb213Pact" toText Chainweb214Pact = "chainweb214Pact" toText Chainweb215Pact = "chainweb215Pact" + toText Pact44NewTrans = "pact44NewTrans" toText Chainweb216Pact = "chainweb216Pact" toText Chainweb217Pact = "chainweb217Pact" toText Chainweb218Pact = "chainweb218Pact" - toText Pact44NewTrans = "pact44NewTrans" + fromText "slowEpoch" = return SlowEpoch fromText "vuln797Fix" = return Vuln797Fix fromText "coinV2" = return CoinV2 - fromText "slowEpoch" = return SlowEpoch + fromText "pactBackCompat_v16" = return PactBackCompat_v16 + fromText "moduleNameFix" = return ModuleNameFix + fromText "skipTxTimingValidation" = return SkipTxTimingValidation fromText "oldTargetGuard" = return OldTargetGuard - fromText "enforceKeysetFormats" = return EnforceKeysetFormats fromText "skipFeatureFlagValidation" = return SkipFeatureFlagValidation + fromText "moduleNameFix2" = return ModuleNameFix2 fromText "oldDaGuard" = return OldDAGuard - fromText "checkTxHash" = return CheckTxHash - fromText "pact4Coin3" = return Pact4Coin3 fromText "pactEvents" = return PactEvents - fromText "skipTxTimingValidation" = return SkipTxTimingValidation fromText "spvBridge" = return SPVBridge - fromText "pactBackCompat_v16" = return PactBackCompat_v16 - fromText "moduleNameFix" = return ModuleNameFix - fromText "moduleNameFix2" = return ModuleNameFix2 + fromText "pact4Coin3" = return Pact4Coin3 + fromText "enforceKeysetFormats" = return EnforceKeysetFormats fromText "pact420" = return Pact420 + fromText "checkTxHash" = return CheckTxHash fromText "chainweb213Pact" = return Chainweb213Pact fromText "chainweb214Pact" = return Chainweb214Pact fromText "chainweb215Pact" = return Chainweb215Pact + fromText "pact44NewTrans" = return Pact44NewTrans fromText "chainweb216Pact" = return Chainweb216Pact fromText "chainweb217Pact" = return Chainweb217Pact fromText "chainweb218Pact" = return Chainweb218Pact - fromText "pact44NewTrans" = return Pact44NewTrans fromText t = throwM . TextFormatException $ "Unknown Chainweb fork: " <> t instance ToJSON Fork where diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 7d0fc244ef..66db771c02 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -41,29 +41,29 @@ devnet = ChainwebVersion , _versionName = ChainwebVersionName "development" , _versionForks = tabulateHashMap $ \case - Vuln797Fix -> AllChains 0 SlowEpoch -> AllChains 0 + Vuln797Fix -> AllChains 0 + CoinV2 -> onChains $ concat + [ [(unsafeChainId 0, BlockHeight 3)] + , [(unsafeChainId i, BlockHeight 4) | i <- [1..19]] + ] + PactBackCompat_v16 -> AllChains 1 + ModuleNameFix -> AllChains 1 + SkipTxTimingValidation -> AllChains 1 OldTargetGuard -> AllChains 0 - EnforceKeysetFormats -> AllChains 1 SkipFeatureFlagValidation -> AllChains 1 + ModuleNameFix2 -> AllChains 1 OldDAGuard -> AllChains 13 - CheckTxHash -> AllChains 1 PactEvents -> AllChains 1 - SkipTxTimingValidation -> AllChains 1 SPVBridge -> AllChains 1 - ModuleNameFix -> AllChains 1 - ModuleNameFix2 -> AllChains 1 - PactBackCompat_v16 -> AllChains 1 - Pact44NewTrans -> AllChains 1 - Pact420 -> AllChains $ BlockHeight 1 - CoinV2 -> onChains $ concat - [ [(unsafeChainId 0, BlockHeight 3)] - , [(unsafeChainId i, BlockHeight 4) | i <- [1..19]] - ] Pact4Coin3 -> AllChains $ BlockHeight 14 + EnforceKeysetFormats -> AllChains 1 + Pact420 -> AllChains $ BlockHeight 1 + CheckTxHash -> AllChains 1 Chainweb213Pact -> AllChains $ BlockHeight 15 Chainweb214Pact -> AllChains $ BlockHeight 16 Chainweb215Pact -> AllChains $ BlockHeight 17 + Pact44NewTrans -> AllChains 1 Chainweb216Pact -> AllChains $ BlockHeight 18 Chainweb217Pact -> AllChains $ BlockHeight 18 Chainweb218Pact -> AllChains $ BlockHeight 18 diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 026ac00287..1a31127d96 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -93,10 +93,7 @@ mainnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000005 , _versionName = ChainwebVersionName "mainnet01" , _versionForks = tabulateHashMap $ \case - SlowEpoch -> AllChains 80_000 - OldTargetGuard -> AllChains 452_820 -- ~ 2020-04-04T00:00:00Z - SkipFeatureFlagValidation -> AllChains 530_500 -- ~ 2020-05-01T00:00:xxZ - OldDAGuard -> AllChains 771_414 -- ~ 2020-07-23 16:00:00 + SlowEpoch -> AllChains (BlockHeight 80_000) Vuln797Fix -> onChains $ [ (unsafeChainId 0, BlockHeight 121_452) -- 2019-12-10T21:00:00.0 , (unsafeChainId 1, BlockHeight 121_452) @@ -109,18 +106,7 @@ mainnet = ChainwebVersion , (unsafeChainId 8, BlockHeight 121_452) , (unsafeChainId 9, BlockHeight 121_451) ] <> [(unsafeChainId i, BlockHeight 0) | i <- [10..19]] - PactBackCompat_v16 -> AllChains 328_000 - SkipTxTimingValidation -> AllChains 449_940 - ModuleNameFix -> AllChains 448_501 - ModuleNameFix2 -> AllChains 752_214 - PactEvents -> AllChains 1_138_000 - SPVBridge -> AllChains 1_275_000 - EnforceKeysetFormats -> AllChains 2_162_000 -- 2022-01-17T17:51:12 - CheckTxHash -> AllChains 2_349_800 -- 2022-01-23T02:53:38 - Pact44NewTrans -> AllChains 2_965_885 -- Todo: add date - Pact420 -> AllChains (BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 - - CoinV2 -> onChains + CoinV2 -> onChains $ [ (unsafeChainId 0, BlockHeight 140_808) , (unsafeChainId 1, BlockHeight 140_809) , (unsafeChainId 2, BlockHeight 140_808) @@ -131,11 +117,24 @@ mainnet = ChainwebVersion , (unsafeChainId 7, BlockHeight 140_809) , (unsafeChainId 8, BlockHeight 140_808) , (unsafeChainId 9, BlockHeight 140_808) - ] + ] <> [(unsafeChainId i, BlockHeight 1) | i <- [10..19]] + PactBackCompat_v16 -> AllChains (BlockHeight 328_000) + ModuleNameFix -> AllChains (BlockHeight 448_501) + SkipTxTimingValidation -> AllChains (BlockHeight 449_940) + OldTargetGuard -> AllChains (BlockHeight 452_820) -- ~ 2020-04-04T00:00:00Z + SkipFeatureFlagValidation -> AllChains (BlockHeight 530_500) -- ~ 2020-05-01T00:00:xxZ + ModuleNameFix2 -> AllChains (BlockHeight 752_214) + OldDAGuard -> AllChains (BlockHeight 771_414) -- ~ 2020-07-23 16:00:00 + PactEvents -> AllChains (BlockHeight 1_138_000) + SPVBridge -> AllChains (BlockHeight 1_275_000) Pact4Coin3 -> AllChains (BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 + EnforceKeysetFormats -> AllChains (BlockHeight 2_162_000) -- 2022-01-17T17:51:12 + Pact420 -> AllChains (BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 + CheckTxHash -> AllChains (BlockHeight 2_349_800) -- 2022-01-23T02:53:38 Chainweb213Pact -> AllChains (BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 Chainweb214Pact -> AllChains (BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 Chainweb215Pact -> AllChains (BlockHeight 2_766_630) -- 2022-06-17T00:00:00+00:00 + Pact44NewTrans -> AllChains (BlockHeight 2_965_885) -- Todo: add date Chainweb216Pact -> AllChains (BlockHeight 2_988_324) -- 2022-09-02T00:00:00+00:00 Chainweb217Pact -> AllChains (BlockHeight 3_250_348) -- 2022-12-02T00:00:00+00:00 Chainweb218Pact -> AllChains (BlockHeight 3_512_363) -- 2023-03-03 00:00:00+00:00 diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index 5562b97e3b..46626460a4 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -93,29 +93,28 @@ testnet = ChainwebVersion , _versionName = ChainwebVersionName "testnet04" , _versionForks = tabulateHashMap $ \case SlowEpoch -> AllChains 0 - OldTargetGuard -> AllChains 0 - SkipFeatureFlagValidation -> AllChains 0 - OldDAGuard -> AllChains 318_204 -- ~ 2020-07-23 16:00:00 Vuln797Fix -> AllChains 0 + CoinV2 -> onChains $ concat + [ [(unsafeChainId i, BlockHeight 1) | i <- [0..9]] + , [(unsafeChainId i, BlockHeight 337_000) | i <- [10..19]] + ] PactBackCompat_v16 -> AllChains 0 - SkipTxTimingValidation -> AllChains 1 ModuleNameFix -> AllChains 2 + SkipTxTimingValidation -> AllChains 1 + OldTargetGuard -> AllChains 0 + SkipFeatureFlagValidation -> AllChains 0 ModuleNameFix2 -> AllChains 289_966 -- ~ 2020-07-13 + OldDAGuard -> AllChains 318_204 -- ~ 2020-07-23 16:00:00 PactEvents -> AllChains 660_000 SPVBridge -> AllChains 820_000 -- 2021-01-14T17:12:02 + Pact4Coin3 -> AllChains 1_261_000 -- 2021-06-17T15:54:14 EnforceKeysetFormats -> AllChains 1_701_000 -- 2021-11-18T17:54:36 - CheckTxHash -> AllChains 1_889_000 -- 2022-01-24T04:19:24 - Pact44NewTrans -> AllChains 2_500_369 -- Todo: add date Pact420 -> AllChains 1_862_000 -- 2021-06-19T03:34:05 - - CoinV2 -> onChains $ concat - [ [(unsafeChainId i, BlockHeight 1) | i <- [0..9]] - , [(unsafeChainId i, BlockHeight 337_000) | i <- [10..19]] - ] - Pact4Coin3 -> AllChains 1_261_000 -- 2021-06-17T15:54:14 + CheckTxHash -> AllChains 1_889_000 -- 2022-01-24T04:19:24 Chainweb213Pact -> AllChains 1_974_556 -- 2022-02-25 00:00:00 Chainweb214Pact -> AllChains 2_134_331 -- 2022-04-21T12:00:00Z Chainweb215Pact -> AllChains 2_295_437 -- 2022-06-16T12:00:00+00:00 + Pact44NewTrans -> AllChains 2_500_369 -- Todo: add date Chainweb216Pact -> AllChains 2_516_739 -- 2022-09-01 12:00:00+00:00 Chainweb217Pact -> AllChains 2_777_367 -- 2022-12-01 12:00:00+00:00 Chainweb218Pact -> AllChains 3_038_343 -- 2023-03-02 12:00:00+00:00 diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index a14e53e548..678d5ed257 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -107,31 +107,30 @@ testVersionTemplate v = v & versionBootstraps .~ [testBootstrapPeerInfos] fastForks :: HashMap Fork (ChainMap BlockHeight) -fastForks = HM.fromList - [ (Pact420, AllChains (BlockHeight 0)) - , (SlowEpoch, AllChains (BlockHeight 0)) - , (OldTargetGuard, AllChains (BlockHeight 0)) - , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) - , (OldDAGuard, AllChains (BlockHeight 0)) - , (Vuln797Fix, AllChains (BlockHeight 0)) - , (PactBackCompat_v16, AllChains (BlockHeight 0)) - , (SPVBridge, AllChains (BlockHeight 0)) - , (EnforceKeysetFormats, AllChains (BlockHeight 0)) - , (CheckTxHash, AllChains (BlockHeight 0)) - , (Pact44NewTrans, AllChains (BlockHeight 0)) - , (Chainweb213Pact, AllChains (BlockHeight 0)) - , (PactEvents, AllChains (BlockHeight 0)) - , (CoinV2, AllChains (BlockHeight 1)) - , (SkipTxTimingValidation, AllChains (BlockHeight 2)) - , (ModuleNameFix, AllChains (BlockHeight 2)) - , (ModuleNameFix2, AllChains (BlockHeight 2)) - , (Pact4Coin3, AllChains (BlockHeight 4)) - , (Chainweb214Pact, AllChains (BlockHeight 5)) - , (Chainweb215Pact, AllChains (BlockHeight 10)) - , (Chainweb216Pact, AllChains (BlockHeight 16)) - , (Chainweb217Pact, AllChains (BlockHeight 20)) - , (Chainweb218Pact, AllChains (BlockHeight 21)) - ] +fastForks = tabulateHashMap $ \case + Pact420 -> AllChains (BlockHeight 0) + SlowEpoch -> AllChains (BlockHeight 0) + OldTargetGuard -> AllChains (BlockHeight 0) + SkipFeatureFlagValidation -> AllChains (BlockHeight 0) + OldDAGuard -> AllChains (BlockHeight 0) + Vuln797Fix -> AllChains (BlockHeight 0) + PactBackCompat_v16 -> AllChains (BlockHeight 0) + SPVBridge -> AllChains (BlockHeight 0) + EnforceKeysetFormats -> AllChains (BlockHeight 0) + CheckTxHash -> AllChains (BlockHeight 0) + Pact44NewTrans -> AllChains (BlockHeight 0) + Chainweb213Pact -> AllChains (BlockHeight 0) + PactEvents -> AllChains (BlockHeight 0) + CoinV2 -> AllChains (BlockHeight 1) + SkipTxTimingValidation -> AllChains (BlockHeight 2) + ModuleNameFix -> AllChains (BlockHeight 2) + ModuleNameFix2 -> AllChains (BlockHeight 2) + Pact4Coin3 -> AllChains (BlockHeight 4) + Chainweb214Pact -> AllChains (BlockHeight 5) + Chainweb215Pact -> AllChains (BlockHeight 10) + Chainweb216Pact -> AllChains (BlockHeight 16) + Chainweb217Pact -> AllChains (BlockHeight 20) + Chainweb218Pact -> AllChains (BlockHeight 21) barebonesTestVersion :: ChainGraph -> ChainwebVersion barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) From a517a118bd72745cf5b89f46fc523c34d934c3d0 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 19:21:11 -0500 Subject: [PATCH 29/91] cleanup and docs --- src/Chainweb/ChainId.hs | 5 +- src/Chainweb/Cut/Create.hs | 2 +- src/Chainweb/CutDB.hs | 2 +- src/Chainweb/Difficulty.hs | 15 +- src/Chainweb/Pact/RestAPI/Server.hs | 2 +- src/Chainweb/RestAPI/NodeInfo.hs | 3 +- src/Chainweb/SPV/CreateProof.hs | 6 +- src/Chainweb/Utils/Rule.hs | 20 ++- src/Chainweb/Version.hs | 165 +++++++++++----------- src/Chainweb/Version/Registry.hs | 38 ++++- test/Chainweb/Test/MultiNode.hs | 2 +- test/Chainweb/Test/Pact/RemotePactTest.hs | 13 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 1 - test/Chainweb/Test/SPV.hs | 2 +- test/Chainweb/Test/TestVersions.hs | 20 ++- test/Chainweb/Test/Utils.hs | 1 - 16 files changed, 158 insertions(+), 139 deletions(-) diff --git a/src/Chainweb/ChainId.hs b/src/Chainweb/ChainId.hs index 2933f47a6b..473ee6ec73 100644 --- a/src/Chainweb/ChainId.hs +++ b/src/Chainweb/ChainId.hs @@ -267,14 +267,16 @@ chainIdInt :: Integral i => ChainId -> i chainIdInt (ChainId cid) = int cid {-# INLINE chainIdInt #-} --- edtodo: document +-- | Values keyed by `ChainId`s, or a single value that applies for all chains. data ChainMap a = AllChains a | OnChains (HashMap ChainId a) deriving stock (Eq, Functor, Foldable, Generic, Ord, Show) deriving anyclass (Hashable, NFData) +-- | A smart constructor, @onChains = OnChains . HM.fromList@. onChains :: [(ChainId, a)] -> ChainMap a onChains = OnChains . HM.fromList +-- | Zips two `ChainMap`s on their chain IDs. chainZip :: (a -> a -> a) -> ChainMap a -> ChainMap a -> ChainMap a chainZip f (OnChains l) (OnChains r) = OnChains $ HM.unionWith f l r chainZip f (OnChains l) (AllChains r) = OnChains $ fmap (`f` r) l @@ -293,6 +295,7 @@ instance FromJSON a => FromJSON (ChainMap a) where makePrisms ''ChainMap +-- | Provides access to the value at a `ChainId`, if it exists. onChain :: ChainId -> Fold (ChainMap a) a onChain cid = folding $ \case OnChains m -> m ^. at cid diff --git a/src/Chainweb/Cut/Create.hs b/src/Chainweb/Cut/Create.hs index 042da19b76..a0a6410060 100644 --- a/src/Chainweb/Cut/Create.hs +++ b/src/Chainweb/Cut/Create.hs @@ -187,7 +187,7 @@ getCutExtension c cid = do v = _chainwebVersion c parentHeight = _blockHeight p targetHeight = parentHeight + 1 - parentGraph = chainGraphAt_ p parentHeight + parentGraph = chainGraphAt p parentHeight isGraphTransitionPost = isGraphChange c parentHeight isGraphTransitionCut = isTransitionCut c diff --git a/src/Chainweb/CutDB.hs b/src/Chainweb/CutDB.hs index 6dd2aa67bd..7beb82ca7a 100644 --- a/src/Chainweb/CutDB.hs +++ b/src/Chainweb/CutDB.hs @@ -612,7 +612,7 @@ processCuts conf logFun headerStore payloadStore cutHashesStore queue cutVar = d -- isVeryOld x = do curMin <- minChainHeight <$> readTVarIO cutVar - let diam = diameter $ chainGraphAt_ headerStore curMin + let diam = diameter $ chainGraphAt headerStore curMin newMin = _cutHashesMinHeight x let r = newMin + 2 * (1 + int diam) <= curMin when r $ loggc Debug x "skip very old cut" diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index 40cba31fb5..76a2894975 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -99,17 +99,16 @@ import Numeric.Natural instance NFData Word128 instance NFData Word256 -- --- -- | The gap in SECONDS that we desire between the Creation Time of subsequent --- -- blocks in some chain. --- -- +-- | The gap in MICROSECONDS that we desire between the Creation Time of subsequent +-- blocks in some chain. +-- newtype BlockRate = BlockRate { _getBlockRate :: Micros } deriving stock (Eq, Generic, Ord, Show) deriving newtype (Hashable, NFData, ToJSON, FromJSON) --- -- | The number of blocks to be mined after a difficulty adjustment, before --- -- considering a further adjustment. Critical for the "epoch-based" adjustment --- -- algorithm seen in `adjust`. --- -- +-- | The number of blocks to be mined after a difficulty adjustment, before +-- considering a further adjustment. Critical for the "epoch-based" adjustment +-- algorithm seen in `adjust`. newtype WindowWidth = WindowWidth Natural deriving stock (Eq, Generic, Ord, Show) deriving newtype (Hashable, NFData, ToJSON, FromJSON) @@ -331,7 +330,7 @@ adjust (BlockRate br) (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = -- This is used when 'oldDaGuard' is active. -- legacyAdjust - :: HasCallStack + :: HasCallStack => BlockRate -> WindowWidth -> TimeSpan Micros diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index d4dcb44ae4..d6ae595860 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -495,7 +495,7 @@ spv2Handler l cdb cid r = case _spvSubjectIdType sid of Nothing -> toErr $ "Transaction hash not found: " <> sshow ph Just t -> return t - let confDepth = fromMaybe (diameter (chainGraphAt_ cdb bhe)) $ _spv2ReqMinimalProofDepth r + let confDepth = fromMaybe (diameter (chainGraphAt cdb bhe)) $ _spv2ReqMinimalProofDepth r liftIO (tryAllSynchronous $ f bdb pdb confDepth bha rk) >>= \case Left e -> toErr $ "SPV proof creation failed:" <> sshow e diff --git a/src/Chainweb/RestAPI/NodeInfo.hs b/src/Chainweb/RestAPI/NodeInfo.hs index af5c7f1a2e..9dcf13c176 100644 --- a/src/Chainweb/RestAPI/NodeInfo.hs +++ b/src/Chainweb/RestAPI/NodeInfo.hs @@ -57,7 +57,8 @@ data NodeInfo = NodeInfo -- ^ List of chain graphs and the block height they took effect. Sorted -- descending by height so the current chain graph is at the beginning. , nodeLatestBehaviorHeight :: BlockHeight - -- ^ edtodo document + -- ^ Height at which the latest behavior of the node is activated. See + -- `Chainweb.Version.latestBehaviorAt`. } deriving (Show, Eq, Generic) instance ToJSON NodeInfo diff --git a/src/Chainweb/SPV/CreateProof.hs b/src/Chainweb/SPV/CreateProof.hs index a9af285b09..424d570d4b 100644 --- a/src/Chainweb/SPV/CreateProof.hs +++ b/src/Chainweb/SPV/CreateProof.hs @@ -444,7 +444,7 @@ crumbsToChain db srcCid trgHeader | (int (_blockHeight trgHeader) + 1) < length path = return Nothing | otherwise = Just <$> go trgHeader path [] where - graph = chainGraphAt_ db (_blockHeight trgHeader) + graph = chainGraphAt db (_blockHeight trgHeader) path = shortestPath (_chainId trgHeader) srcCid graph go @@ -492,8 +492,8 @@ minimumTrgHeader headerDb tcid scid bh = do -- This assumes that graph changes are at least graph-diameter -- blocks appart. - srcGraph = chainGraphAt_ headerDb bh + srcGraph = chainGraphAt headerDb bh srcDistance = length $ shortestPath tcid scid srcGraph - trgGraph = chainGraphAt_ headerDb (bh + int srcDistance) + trgGraph = chainGraphAt headerDb (bh + int srcDistance) trgDistance = length $ shortestPath tcid scid trgGraph diff --git a/src/Chainweb/Utils/Rule.hs b/src/Chainweb/Utils/Rule.hs index 9b0d24321f..d97110f522 100644 --- a/src/Chainweb/Utils/Rule.hs +++ b/src/Chainweb/Utils/Rule.hs @@ -6,8 +6,6 @@ {-# language DerivingStrategies #-} {-# language TupleSections #-} --- edTODO document - module Chainweb.Utils.Rule where import Control.DeepSeq @@ -22,6 +20,9 @@ import qualified Data.Vector as V import GHC.Generics +-- | `a` values graded by `h`, starting with the highest `h` value and lowering +-- as you go deeper, bottoming out with no `h` value at all. Used to efficiently +-- represent behaviors that change as the block height increases. data Rule h a = Above (h, a) (Rule h a) | End a deriving stock (Eq, Ord, Show, Foldable, Functor, Generic, Generic1, Traversable) deriving anyclass (Hashable, NFData) @@ -60,8 +61,11 @@ ruleDropWhile p (Above (h, a) t) | otherwise = Above (h, a) t ruleDropWhile _ t = t +-- | A measurement on a rule tells you where a condition starts to be true; at +-- the Top, at the Bottom, or Between lower and upper. data Measurement h a = Bottom a | Top (h, a) | Between (h, a) (h, a) +-- | Takes a measurement on a rule using a monotone function. measureRule' :: (h -> Bool) -> Rule h a -> Measurement h a measureRule' p ((topH, topA) `Above` topTail) | p topH = Top (topH, topA) @@ -77,18 +81,12 @@ measureRule :: Ord h => h -> Rule h a -> Measurement h a measureRule h = measureRule' (\hc -> h >= hc) -ruleElemHeight :: Eq a => (a -> Bool) -> Rule h a -> Maybe (Maybe h) -ruleElemHeight p (Above (h, a) t) - | p a = Just (Just h) - | otherwise = ruleElemHeight p t -ruleElemHeight p (End a) - | p a = Just Nothing - | otherwise = Nothing - +-- | Returns the elements of the Rule. ruleElems :: h -> Rule h a -> NE.NonEmpty (h, a) ruleElems h (End a) = (h, a) NE.:| [] ruleElems he (Above (h, a) t) = (h, a) `NE.cons` ruleElems he t +-- | Checks that a Rule is decreasing, and thus valid. ruleValid :: Ord h => Rule h a -> Bool -ruleValid (Above (h, _) t@(Above (h', _) _)) = h >= h' && ruleValid t +ruleValid (Above (h, _) t@(Above (h', _) _)) = h > h' && ruleValid t ruleValid _ = True \ No newline at end of file diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 62091e117f..a8182b3f99 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -1,29 +1,17 @@ --- edtodo redo docs here -{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} -{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE RankNTypes #-} -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} @@ -73,7 +61,7 @@ module Chainweb.Version , genesisBlockTarget , genesisTime - -- * Typelevel ChainwebVersion + -- * Typelevel ChainwebVersionName , ChainwebVersionT(..) , ChainwebVersionSymbol , chainwebVersionSymbolVal @@ -91,7 +79,7 @@ module Chainweb.Version , mkChainId , chainIds - -- * ChainId + -- * ChainId re-export , module Chainweb.ChainId -- * Re-exports from Chainweb.ChainGraph @@ -101,7 +89,6 @@ module Chainweb.Version , HasChainGraph(..) , adjacentChainIds , chainGraphAt - , chainGraphAt_ , chainwebGraphsAt -- ** Graph Properties @@ -144,7 +131,6 @@ import qualified Data.Text as T import Data.Word import GHC.Generics(Generic) -import GHC.Stack import GHC.TypeLits import Numeric.Natural @@ -169,14 +155,8 @@ import Data.Singletons import P2P.Peer --- -------------------------------------------------------------------------- -- --- Bootstrap Peer Info - --- | Official testnet bootstrap nodes --- -domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] -domainAddr2PeerInfo = fmap (PeerInfo Nothing) - +-- | Data type representing changes to block validation, whether in the payload or in the header. +-- Always add new forks at the end, not in the middle of the constructors. data Fork = SlowEpoch | Vuln797Fix @@ -202,7 +182,8 @@ data Fork | Chainweb217Pact | Chainweb218Pact -- always add new forks at the end, not in the middle of the constructors. - deriving (Bounded, Generic, NFData, Hashable, Eq, Enum, Ord, Show) + deriving stock (Bounded, Generic, Eq, Enum, Ord, Show) + deriving anyclass (NFData, Hashable) instance HasTextRepresentation Fork where toText SlowEpoch = "slowEpoch" @@ -288,6 +269,8 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVer toMerkleNode = encodeMerkleInputNode encodeChainwebVersionCode fromMerkleNode = decodeMerkleInputNode decodeChainwebVersionCode +-- The type of upgrades, which are sets of transactions to run at certain block +-- heights during coinbase. data Upgrade = Upgrade { _upgradeTransactions :: [ChainwebTransaction] , _legacyUpgradeIsPrecocious :: Bool @@ -302,15 +285,31 @@ data Upgrade = Upgrade upgrade :: [ChainwebTransaction] -> Upgrade upgrade txs = Upgrade txs False +-- | 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.Development`, and +-- `Chainweb.Test.TestVersions`. data ChainwebVersion = ChainwebVersion { _versionCode :: ChainwebVersionCode + -- ^ The numeric code identifying the Version, must be unique. See `Chainweb.Version.Registry`. , _versionName :: ChainwebVersionName + -- ^ The textual name of the Version, used in almost all REST endpoints. , _versionGraphs :: Rule BlockHeight ChainGraph + -- ^ The chain graphs in the history and at which block heights they apply. , _versionForks :: HashMap Fork (ChainMap BlockHeight) + -- ^ The block heights on each chain to apply behavioral changes. + -- Interpretation of these is up to the functions in + -- `Chainweb.Version.Guards`. , _versionUpgrades :: ChainMap (HashMap BlockHeight Upgrade) + -- ^ The upgrade transactions to execute on each chain at certain block heights. , _versionBlockRate :: BlockRate + -- ^ The Proof-of-Work `BlockRate` for each `ChainwebVersion`. This is the + -- number of microseconds we expect to pass while a miner mines on various chains, + -- eventually succeeding on one. , _versionWindow :: Maybe WindowWidth + -- ^ The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. For chainwebs + -- that do not expect to perform POW, this should be `Nothing`. , _versionHeaderBaseSizeBytes :: Natural -- ^ The size in bytes of the constant portion of the serialized header. This is -- the header /without/ the adjacent hashes. @@ -318,10 +317,15 @@ data ChainwebVersion -- NOTE: This is internal. For the actual size of the serialized header -- use 'headerSizeBytes'. , _versionMaxBlockGasLimit :: Rule BlockHeight (Maybe Natural) + -- ^ The maximum gas limit for an entire block. , _versionFakeFirstEpochStart :: Bool + -- ^ Whether to fake the start time of the first epoch. See `Chainweb.BlockHeader.epochStart`. , _versionBootstraps :: [PeerInfo] + -- ^ The locations of the bootstrap peers. , _versionGenesis :: ChainwebGenesis + -- ^ The information used to construct the genesis blocks. , _versionCheats :: Cheats + -- ^ Whether to disable any core functionality. } deriving stock (Generic) deriving anyclass NFData @@ -352,12 +356,15 @@ instance Eq ChainwebVersion where v == v' = and [ compare v v' == EQ , _versionUpgrades v == _versionUpgrades v' + , _versionGenesis v == _versionGenesis v' ] data Cheats = Cheats { _disablePow :: Bool + -- ^ should we stop checking proof of work? , _disablePact :: Bool , _disablePeerValidation :: Bool + -- ^ should we try to check that a peer is valid? See `P2P.Peer.validatePeerConfig`. , _disableMempool :: Bool } deriving stock (Generic, Eq, Ord, Show) @@ -385,8 +392,8 @@ instance HasTextRepresentation ChainwebVersionName where toText = getChainwebVersionName fromText = pure . ChainwebVersionName --- -- -------------------------------------------------------------------------- -- --- -- Type level ChainwebVersion +-------------------------------------------------------------------------- -- +-- Type level ChainwebVersion newtype ChainwebVersionT = ChainwebVersionT Symbol @@ -408,8 +415,8 @@ someChainwebVersionVal' :: ChainwebVersionName -> SomeChainwebVersionT someChainwebVersionVal' v = case someSymbolVal (show v) of (SomeSymbol (Proxy :: Proxy v)) -> SomeChainwebVersionT (Proxy @('ChainwebVersionT v)) --- -- -------------------------------------------------------------------------- -- --- -- Singletons +-------------------------------------------------------------------------- -- +-- Singletons data instance Sing (v :: ChainwebVersionT) where SChainwebVersion :: KnownChainwebVersionSymbol v => Sing v @@ -432,9 +439,9 @@ pattern FromSingChainwebVersion :: Sing (n :: ChainwebVersionT) -> ChainwebVersi pattern FromSingChainwebVersion sng <- ((\v -> withSomeSing (_versionName v) SomeSing) -> SomeSing sng) {-# COMPLETE FromSingChainwebVersion #-} --- -- -------------------------------------------------------------------------- -- --- -- HasChainwebVersion Class --- +-------------------------------------------------------------------------- -- +-- HasChainwebVersion Class + class HasChainwebVersion a where _chainwebVersion :: a -> ChainwebVersion _chainwebVersion = view chainwebVersion @@ -452,13 +459,52 @@ instance HasChainwebVersion ChainwebVersion where chainIds :: HasChainwebVersion v => v -> HS.HashSet ChainId chainIds = graphChainIds . snd . ruleHead . _versionGraphs . _chainwebVersion +mkChainId + :: (MonadThrow m, HasChainwebVersion v) + => v -> BlockHeight -> Word32 -> m ChainId +mkChainId v h i = cid + <$ checkWebChainId (chainGraphAt (_chainwebVersion v) h) cid + where + cid = unsafeChainId i + +-------------------------------------------------------------------------- -- +-- Properties of Chainweb Versions +-------------------------------------------------------------------------- -- + +-- | Return the Graph History at a given block height in descending order. +-- +-- The functions provided in 'Chainweb.Version.Utils' are generally more +-- convenient to use than this function. +-- +-- This function is safe because of invariants provided by 'chainwebGraphs'. +-- (There are also unit tests the confirm this.) +chainwebGraphsAt + :: ChainwebVersion + -> BlockHeight + -> Rule BlockHeight ChainGraph +chainwebGraphsAt v h = + ruleDropWhile (> h) (_versionGraphs v) + +-- | The 'ChainGraph' for the given 'BlockHeight' +chainGraphAt :: HasChainwebVersion v => v -> BlockHeight -> ChainGraph +chainGraphAt v = snd . ruleHead . chainwebGraphsAt (_chainwebVersion v) + +instance HasChainGraph (ChainwebVersion, BlockHeight) where + _chainGraph = uncurry chainGraphAt + +-------------------------------------------------------------------------- -- +-- Utilities for constructing chainweb versions + +domainAddr2PeerInfo :: [HostAddress] -> [PeerInfo] +domainAddr2PeerInfo = fmap (PeerInfo Nothing) + -- | Creates a map from fork heights to upgrades. forkUpgrades :: ChainwebVersion -> [(Fork, ChainMap Upgrade)] -> ChainMap (HashMap BlockHeight Upgrade) forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) - where + where conflictError fork h = error $ "conflicting upgrades at block height " <> show h <> " when adding upgrade for fork " <> show fork emptyUpgradeError fork = @@ -467,7 +513,7 @@ forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) HM.unionWith (HM.unionWithKey (conflictError fork)) acc newTxs - where + where newTxs = HM.fromList $ [ (cid, HM.singleton forkHeight upg) | cid <- HM.keys acc @@ -480,57 +526,10 @@ forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) -- | The block height at all chains at which the latest known behavior changes -- will have taken effect: forks, upgrade transactions, or graph changes. latestBehaviorAt :: ChainwebVersion -> BlockHeight -latestBehaviorAt v = - foldlOf' changes max 0 v + 1 +latestBehaviorAt v = foldlOf' behaviorChanges max 0 v + 1 where - changes = fold + behaviorChanges = fold [ versionForks . folded . folded , versionUpgrades . folded . ifolded . asIndex , versionGraphs . to ruleHead . _1 . _Just ] . filtered (/= maxBound) - -mkChainId - :: (MonadThrow m, HasChainwebVersion v) - => v -> BlockHeight -> Word32 -> m ChainId -mkChainId v h i = cid - <$ checkWebChainId (chainGraphAt (_chainwebVersion v) h) cid - where - cid = unsafeChainId i - --- -- -------------------------------------------------------------------------- -- --- -- Properties of Chainweb Versions --- -- -------------------------------------------------------------------------- -- - --- | Return the Graph History at a given block height in descending order. --- --- The functions provided in 'Chainweb.Version.Utils' are generally more --- convenient to use than this function. --- --- This function is safe because of invariants provided by 'chainwebGraphs'. --- (There are also unit tests the confirm this.) --- -chainwebGraphsAt - :: HasCallStack - => ChainwebVersion - -> BlockHeight - -> Rule BlockHeight ChainGraph -chainwebGraphsAt v h = - ruleDropWhile (> h) (_versionGraphs v) - --- | The 'ChainGraph' for the given 'BlockHeight' --- -chainGraphAt :: HasCallStack => ChainwebVersion -> BlockHeight -> ChainGraph -chainGraphAt v = snd . ruleHead . chainwebGraphsAt v - --- | The 'ChainGraph' for the given 'BlockHeight' --- -chainGraphAt_ - :: HasCallStack - => HasChainwebVersion v - => v - -> BlockHeight - -> ChainGraph -chainGraphAt_ = chainGraphAt . _chainwebVersion - -instance HasChainGraph (ChainwebVersion, BlockHeight) where - _chainGraph = uncurry chainGraphAt diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index b72d1bf8ba..1734bf5183 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -25,9 +25,11 @@ module Chainweb.Version.Registry import Control.DeepSeq import Control.Exception import Control.Lens +import Control.Monad import Data.Foldable import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM +import qualified Data.HashSet as HS import Data.IORef import Data.Maybe import qualified Data.Text as T @@ -39,18 +41,19 @@ import Chainweb.Version import Chainweb.Version.Development import Chainweb.Version.Mainnet import Chainweb.Version.Testnet +import Chainweb.Utils.Rule {-# NOINLINE versionMap #-} versionMap :: IORef (HashMap ChainwebVersionCode ChainwebVersion) versionMap = unsafePerformIO $ do - traverse_ (evaluate . rnf) knownVersions + traverse_ validateVersion knownVersions newIORef $ HM.fromList [(_versionCode v, v) | v <- [mainnet, testnet]] -- | Register a version into our registry by code, ensuring it contains no -- errors and there are no others registered with that code. -registerVersion :: ChainwebVersion -> IO () +registerVersion :: HasCallStack => ChainwebVersion -> IO () registerVersion v = do - evaluate (rnf v) + validateVersion v atomicModifyIORef' versionMap $ \m -> case HM.lookup (_versionCode v) m of Just v' @@ -59,6 +62,33 @@ registerVersion v = do Nothing -> (HM.insert (_versionCode v) v m, ()) +validateVersion :: HasCallStack => ChainwebVersion -> IO () +validateVersion v = do + evaluate (rnf v) + let + hasAllChains (AllChains _) = True + hasAllChains (OnChains m) = HS.fromMap (void m) == chainIds v + errors = concat + [ [ "validateVersion: version does not have heights for all forks" + | not (HS.fromMap (void $ _versionForks v) == HS.fromList [minBound :: Fork .. maxBound :: Fork]) ] + , [ "validateVersion: version is missing fork heights for some forks on some chains" + | not (all hasAllChains (_versionForks v)) ] + , [ "validateVersion: chain graphs do not decrease in block height" + | not (ruleValid (_versionGraphs v)) ] + , [ "validateVersion: block gas limits do not decrease in block height" + | not (ruleValid (_versionMaxBlockGasLimit v)) ] + , [ "validateVersion: genesis data is missing for some chains" + | not (and + [ hasAllChains (_genesisBlockPayload $ _versionGenesis v) + , hasAllChains (_genesisBlockTarget $ _versionGenesis v) + , hasAllChains (_genesisTime $ _versionGenesis v) + ])] + ] + if null errors + then return () + else + error $ unlines $ ["errors encountered validating version " <> show v <> ":"] <> errors + -- | Look up a version in the registry by code. lookupVersionByCode :: HasCallStack => ChainwebVersionCode -> ChainwebVersion lookupVersionByCode code @@ -75,7 +105,7 @@ lookupVersionByCode code lookupVersion = unsafeDupablePerformIO $ do m <- readIORef versionMap return $ fromMaybe (error notRegistered) $ HM.lookup code m - notRegistered + notRegistered | code == _versionCode devnet = "devnet version used but not registered, remember to do so after it's configured" | otherwise = "version not registered with code " <> show code <> ", have you seen Chainweb.Test.TestVersions.legalizeTestVersion?" diff --git a/test/Chainweb/Test/MultiNode.hs b/test/Chainweb/Test/MultiNode.hs index 5237accd6e..27ed3011bf 100644 --- a/test/Chainweb/Test/MultiNode.hs +++ b/test/Chainweb/Test/MultiNode.hs @@ -475,7 +475,7 @@ consensusStateSummary s } where cutHeights = _cutHeight <$> _stateCutMap s - graph = chainGraphAt_ s + graph = chainGraphAt s $ maximum . concatMap chainHeights $ toList $ _stateCutMap s diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 6f5be43648..85acb8b4f5 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -695,7 +695,7 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do cenv <- fmap _getServiceClientEnv nio step "positive allocation test: allocation00 release" - p <- try @IO @PactTestFailure $ do + p <- do batch0 <- liftIO $ mkSingletonBatch iot allocation00KeyPair tx0 n0 (pm "allocation00") Nothing @@ -710,11 +710,8 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do testCaseStep "localApiClient: submit local account balance request" liftIO $ localTestToRetry sid cenv (head (toList batch1)) (localAfterBlockHeight 4) - case p of - Left e -> assertFailure $ "test failure: " <> show e - Right cr -> assertEqual "00 expect /local allocation balance" accountInfo (resultOf cr) + assertEqual "00 expect /local allocation balance" accountInfo (resultOf p) - -- edtodo: be more principled about `try`? step "negative allocation test: allocation01 release" do batch0 <- mkSingletonBatch iot allocation01KeyPair tx2 n2 (pm "allocation01") Nothing @@ -730,7 +727,7 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do _ -> assertFailure "unexpected pact result success in negative allocation test" step "positive key-rotation test: allocation2" - r <- try @IO @PactTestFailure $ do + r <- do batch0 <- mkSingletonBatch iot allocation02KeyPair tx3 n3 (pm "allocation02") Nothing @@ -752,9 +749,7 @@ allocationTest iot nio = testCaseSteps "genesis allocation tests" $ \step -> do localTestToRetry sid cenv (head (toList batch2)) (localAfterPollResponse pr) - case r of - Left e -> assertFailure $ "test failure: " <> show e - Right cr -> assertEqual "02 expect /local allocation balance" accountInfo' (resultOf cr) + assertEqual "02 expect /local allocation balance" accountInfo' (resultOf r) where n0 = Just "allocation-0" diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 695c36c900..e518d8f81c 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -372,7 +372,6 @@ block20ChainRemediationTests _ envIo = _ -> assertFailure $ "20 chain remediation block should have at least 2 transactions:" ++ " coinbase + 1 remediations" where - -- edtodo: extract this from the version(?) bhChain20Rem = 2 nidChain3 = NetworkId { _networkId_blockchain = "kadena" diff --git a/test/Chainweb/Test/SPV.hs b/test/Chainweb/Test/SPV.hs index e81f187461..775c5b1a49 100644 --- a/test/Chainweb/Test/SPV.hs +++ b/test/Chainweb/Test/SPV.hs @@ -323,7 +323,7 @@ spvTest rdb v step = do -- distance cutDb h trgChain = length $ shortestPath (_chainId h) trgChain - $ chainGraphAt_ cutDb (_blockHeight h) + $ chainGraphAt cutDb (_blockHeight h) -- Check whether target chain is reachable from the source block -- diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 678d5ed257..5c5a22b337 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -143,7 +143,7 @@ barebonesTestVersion' g v = & versionName .~ ChainwebVersionName ("test-" <> toText g) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing , _disablePact = True , _disableMempool = True , _disablePeerValidation = True @@ -167,7 +167,7 @@ cpmTestVersion g v = v & versionBlockRate .~ BlockRate (Micros 100_000) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing , _disablePact = False , _disableMempool = False , _disablePeerValidation = True @@ -248,20 +248,16 @@ timedConsensusVersion' g1 g2 v = & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) & versionBlockRate .~ BlockRate 1_000_000 & versionWindow .~ Nothing - & versionForks .~ HM.fromList - [ (Vuln797Fix, AllChains (BlockHeight 0)) - , (SkipTxTimingValidation, AllChains (BlockHeight 2)) - , (CheckTxHash, AllChains (BlockHeight 0)) - , (SlowEpoch, AllChains (BlockHeight 0)) - , (OldTargetGuard, AllChains (BlockHeight 0)) - , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) - , (OldDAGuard, AllChains (BlockHeight 0)) - ] + & versionForks .~ tabulateHashMap (\case + SkipTxTimingValidation -> AllChains (BlockHeight 2) + -- pact is disabled, we don't care about pact forks + _ -> AllChains (BlockHeight 0) + ) & versionUpgrades .~ AllChains HM.empty & versionWindow .~ Nothing & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing? edtodo + { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing , _disablePact = True , _disableMempool = True , _disablePeerValidation = True diff --git a/test/Chainweb/Test/Utils.hs b/test/Chainweb/Test/Utils.hs index 3b6520f856..e5b4d77312 100644 --- a/test/Chainweb/Test/Utils.hs +++ b/test/Chainweb/Test/Utils.hs @@ -1005,7 +1005,6 @@ withNodesAtLatestBehavior v testLabel rdb n f = withNodes v testLabel rdb n $ \n -- about 10 seconds. Once initialization is complete even large numbers of empty -- blocks were mined almost instantaneously. -- --- edtodo: move to RestAPI.Utils? awaitBlockHeight :: ChainwebVersion -> (String -> IO ()) From 928030804c2184f30518945c7ccf9cb06e2ea7db Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 19:28:11 -0500 Subject: [PATCH 30/91] Comment Chainweb.Version.Guards --- src/Chainweb/Version/Guards.hs | 118 +++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 14 deletions(-) diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index fce83807ae..4a3b3a6cf8 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -68,57 +68,147 @@ checkFork -> Fork -> ChainwebVersion -> ChainId -> BlockHeight -> Bool checkFork p f v cid h = p h (getForkHeight f v cid) +-- -------------------------------------------------------------------------- -- +-- Header Validation Guards +-- +-- The guards in this section encode when changes to validation rules for data +-- on the chain become effective. +-- +-- Only the following types are allowed as parameters for guards +-- +-- * BlockHeader, +-- * ParentHeader, +-- * BlockCreationTime, and +-- * ParentCreationTime +-- +-- The result is a simple 'Bool'. +-- +-- Guards should have meaningful names and should be used in a way that all +-- places in the code base that depend on the guard should reference the +-- respective guard. That way all dependent code can be easily identified using +-- ide tools, like for instance @grep@. +-- +-- Each guard should have a description that provides background for the change +-- and provides all information needed for maintaining the code or code that +-- depends on it. +-- + +-- | Turn off slow epochs (emergency DA) for blocks. +-- +-- Emergency DA is considered a misfeature. +-- +-- It's intended purpose is to prevent chain hopping attacks, where an attacker +-- temporarily adds a large amount of hash power, thus increasing the +-- difficulty. When the hash power is removed, the remaining hash power may not +-- be enough to reach the next block in reasonable time. +-- +-- In practice, emergency DAs cause more problems than they solve. In +-- particular, they increase the chance of deep forks. Also they make the +-- behavior of the system unpredictable in states of emergency, when stability +-- is usually more important than throughput. +-- +slowEpochGuard + :: ChainwebVersion + -> ChainId + -> BlockHeight + -- ^ BlockHeight of parent Header + -> Bool +slowEpochGuard = checkFork (<) SlowEpoch + +-- | Use the current block time for computing epoch start date and +-- target. +-- +-- When this guard is switched off, there will be a single epoch of just 119 +-- blocks. The target computation won't compensate for that, since the effects +-- are marginal. +-- +oldTargetGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +oldTargetGuard = checkFork (<) OldTargetGuard + +-- | Skip validation of feature flags. +-- +-- Unused feature flag bits are supposed to be set to 0. As of Chainweb 1.7, the +-- Feature Flag bytes and Nonce bytes have switched places in `BlockHeader`. For +-- live chains, enforcing the following condition must be ignored for the +-- historical blocks for which both the Nonce and Flags could be anything. +-- +skipFeatureFlagValidationGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +skipFeatureFlagValidationGuard = checkFork (<) SkipFeatureFlagValidation + +oldDaGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +oldDaGuard = checkFork (<) OldDAGuard + +----------------- +-- Payload validation guards + vuln797Fix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool vuln797Fix = checkFork (>=) Vuln797Fix + +-- | Preserve Pact bugs pre-1.6 chainweb. pactBackCompat_v16 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pactBackCompat_v16 = checkFork (<) PactBackCompat_v16 + +-- | Early versions of chainweb used the creation time of the current header +-- for validation of pact tx creation time and TTL. Nowadays the times of +-- the parent header a used. +-- +-- When this guard is enabled timing validation is skipped. +-- skipTxTimingValidation :: ChainwebVersion -> ChainId -> BlockHeight -> Bool skipTxTimingValidation = checkFork (<) SkipTxTimingValidation + +-- | Checks height after which module name fix in effect. +-- enableModuleNameFix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool enableModuleNameFix = checkFork (>=) ModuleNameFix + +-- | Related, later fix (Pact #801). +-- enableModuleNameFix2 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool enableModuleNameFix2 = checkFork (>=) ModuleNameFix2 + +-- | Turn on pact events in command output. enablePactEvents :: ChainwebVersion -> ChainId -> BlockHeight -> Bool enablePactEvents = checkFork (>=) PactEvents + +-- | Bridge support: ETH and event SPV. enableSPVBridge :: ChainwebVersion -> ChainId -> BlockHeight -> Bool enableSPVBridge = checkFork (>=) SPVBridge + enforceKeysetFormats :: ChainwebVersion -> ChainId -> BlockHeight -> Bool enforceKeysetFormats = checkFork (>=) EnforceKeysetFormats + doCheckTxHash :: ChainwebVersion -> ChainId -> BlockHeight -> Bool doCheckTxHash = checkFork (>=) CheckTxHash + +-- | Fork for musl trans funs pact44NewTrans :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pact44NewTrans = checkFork (>=) Pact44NewTrans -slowEpochGuard - :: ChainwebVersion - -> ChainId - -> BlockHeight - -- ^ BlockHeight of parent Header - -> Bool -slowEpochGuard = checkFork (<) SlowEpoch - -oldTargetGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -oldTargetGuard = checkFork (<) OldTargetGuard -skipFeatureFlagValidationGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -skipFeatureFlagValidationGuard = checkFork (<) SkipFeatureFlagValidation -oldDaGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -oldDaGuard = checkFork (<) OldDAGuard pact4Coin3 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pact4Coin3 = checkFork (>) Pact4Coin3 + pact420 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pact420 = checkFork (>=) Pact420 + chainweb213Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb213Pact = checkFork (>=) Chainweb213Pact + chainweb214Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb214Pact = checkFork (>) Chainweb214Pact + chainweb215Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb215Pact = checkFork (>) Chainweb215Pact + chainweb216Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb216Pact = checkFork (>) Chainweb216Pact + chainweb217Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb217Pact = checkFork (>) Chainweb217Pact + cleanModuleCache :: ChainwebVersion -> ChainId -> BlockHeight -> Bool cleanModuleCache = checkFork (==) Chainweb217Pact + chainweb218Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb218Pact = checkFork (>=) Chainweb218Pact From b95483096c9f8a1833efff703244ae892b2e298b Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sat, 4 Mar 2023 20:08:31 -0500 Subject: [PATCH 31/91] Start doing DA during tests --- src/Chainweb/BlockHeader.hs | 7 ++--- src/Chainweb/Chainweb.hs | 2 +- src/Chainweb/Chainweb/MinerResources.hs | 8 +++--- src/Chainweb/Difficulty.hs | 1 - src/Chainweb/Version.hs | 22 +++++++-------- src/Chainweb/Version/Development.hs | 6 ++-- src/Chainweb/Version/Mainnet.hs | 6 ++-- src/Chainweb/Version/Testnet.hs | 6 ++-- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 10 +++---- test/Chainweb/Test/TestVersions.hs | 28 ++++++++++--------- 10 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 10dd42351e..400a6eef13 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -388,8 +388,7 @@ makeLenses ''BlockHeader -- effectiveWindow :: BlockHeader -> Maybe WindowWidth effectiveWindow h = WindowWidth <$> case _versionWindow (_chainwebVersion h) of - Nothing -> Nothing - Just (WindowWidth w) + WindowWidth w | int (_blockHeight h) <= w -> Just $ max 1 $ w `div` 10 | otherwise -> Just w @@ -416,7 +415,7 @@ slowEpoch (ParentHeader p) (BlockCreationTime ct) = actual > (expected * 5) where EpochStartTime es = _blockEpochStart p BlockRate s = _versionBlockRate (_blockChainwebVersion p) - WindowWidth ww = fromJuste $ _versionWindow (_blockChainwebVersion p) + WindowWidth ww = _versionWindow (_blockChainwebVersion p) expected :: Micros expected = s * int ww @@ -495,7 +494,7 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) -- A special case for starting a new devnet, to compensate the inaccurate -- creation time of the genesis blocks. This would result in a very long -- first epoch that cause a trivial target in the second epoch. - | _versionFakeFirstEpochStart ver, _blockHeight p == 1 = EpochStartTime (_bct $ _blockCreationTime p) + | ver ^. versionCheats . fakeFirstEpochStart, _blockHeight p == 1 = EpochStartTime (_bct $ _blockCreationTime p) -- New Graph: the block time of the genesis block isn't accurate, we thus -- use the block time of the first block on the chain. Depending on where diff --git a/src/Chainweb/Chainweb.hs b/src/Chainweb/Chainweb.hs index d0eb87253c..f20492b19f 100644 --- a/src/Chainweb/Chainweb.hs +++ b/src/Chainweb/Chainweb.hs @@ -826,7 +826,7 @@ runChainweb cw = do mempoolSyncClients = case enabledConfig mempoolP2pConfig of Nothing -> disabled Just c - | cw ^. chainwebVersion . versionCheats . disableMempool -> disabled + | cw ^. chainwebVersion . versionCheats . disableMempoolSync -> disabled | otherwise -> enabled c where disabled = do diff --git a/src/Chainweb/Chainweb/MinerResources.hs b/src/Chainweb/Chainweb/MinerResources.hs index e4201b818f..ecff958819 100644 --- a/src/Chainweb/Chainweb/MinerResources.hs +++ b/src/Chainweb/Chainweb/MinerResources.hs @@ -33,7 +33,7 @@ import Control.Concurrent (threadDelay) import Control.Concurrent.Async import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TVar -import Control.Lens (at, over, view, (&), (?~)) +import Control.Lens (at, over, view, (&), (?~), (^.)) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM @@ -275,9 +275,9 @@ runMiner v mr | enabled = case _minerResCoordination mr of Nothing -> error "Mining coordination must be enabled in order to use the in-node test miner" - Just coord -> case _versionWindow v of - Nothing -> testMiner coord - Just _ -> powMiner coord + Just coord -> case v ^. versionCheats . disablePow of + True -> testMiner coord + False -> powMiner coord | otherwise = mempoolNoopMiner lf (_chainResMempool <$> _minerChainResources mr) where diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index 76a2894975..15d7a8889e 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -5,7 +5,6 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index a8182b3f99..1e0d2b4447 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -32,10 +32,11 @@ module Chainweb.Version Fork(..) , ChainwebGenesis(..) , Cheats(..) - , disableMempool + , disablePow + , fakeFirstEpochStart , disablePact , disablePeerValidation - , disablePow + , disableMempoolSync , ChainwebVersionCode(..) , encodeChainwebVersionCode , decodeChainwebVersionCode @@ -49,7 +50,6 @@ module Chainweb.Version , versionUpgrades , versionBootstraps , versionCode - , versionFakeFirstEpochStart , versionGraphs , versionHeaderBaseSizeBytes , versionMaxBlockGasLimit @@ -307,9 +307,8 @@ data ChainwebVersion -- ^ The Proof-of-Work `BlockRate` for each `ChainwebVersion`. This is the -- number of microseconds we expect to pass while a miner mines on various chains, -- eventually succeeding on one. - , _versionWindow :: Maybe WindowWidth - -- ^ The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. For chainwebs - -- that do not expect to perform POW, this should be `Nothing`. + , _versionWindow :: WindowWidth + -- ^ The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. , _versionHeaderBaseSizeBytes :: Natural -- ^ The size in bytes of the constant portion of the serialized header. This is -- the header /without/ the adjacent hashes. @@ -318,8 +317,6 @@ data ChainwebVersion -- use 'headerSizeBytes'. , _versionMaxBlockGasLimit :: Rule BlockHeight (Maybe Natural) -- ^ The maximum gas limit for an entire block. - , _versionFakeFirstEpochStart :: Bool - -- ^ Whether to fake the start time of the first epoch. See `Chainweb.BlockHeader.epochStart`. , _versionBootstraps :: [PeerInfo] -- ^ The locations of the bootstrap peers. , _versionGenesis :: ChainwebGenesis @@ -345,7 +342,6 @@ instance Ord ChainwebVersion where , _versionWindow v `compare` _versionWindow v' , _versionHeaderBaseSizeBytes v `compare` _versionHeaderBaseSizeBytes v' , _versionMaxBlockGasLimit v `compare` _versionMaxBlockGasLimit v' - , _versionFakeFirstEpochStart v `compare` _versionFakeFirstEpochStart v' , _versionBootstraps v `compare` _versionBootstraps v' -- genesis cannot be ordered because Payload in Pact cannot be ordered -- , _versionGenesis v `compare` _versionGenesis v' @@ -362,10 +358,14 @@ instance Eq ChainwebVersion where data Cheats = Cheats { _disablePow :: Bool -- ^ should we stop checking proof of work? + , _fakeFirstEpochStart :: Bool + -- ^ should we fake the start time of the first epoch? See `Chainweb.BlockHeader.epochStart`. , _disablePact :: Bool + -- ^ Should we replace the pact service with a dummy that always makes empty blocks? , _disablePeerValidation :: Bool - -- ^ should we try to check that a peer is valid? See `P2P.Peer.validatePeerConfig`. - , _disableMempool :: Bool + -- ^ should we try to check that a peer is valid? See `P2P.Peer.validatePeerConfig` + , _disableMempoolSync :: Bool + -- ^ should we disable mempool sync entirely? } deriving stock (Generic, Eq, Ord, Show) deriving anyclass (ToJSON, FromJSON, NFData) diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 66db771c02..024522475f 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -86,9 +86,8 @@ devnet = ChainwebVersion End petersonChainGraph , _versionBlockRate = BlockRate 30_000_000 - , _versionWindow = Just $ WindowWidth 120 + , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 - , _versionFakeFirstEpochStart = True , _versionBootstraps = [] , _versionGenesis = ChainwebGenesis { _genesisBlockTarget = onChains $ concat @@ -107,7 +106,8 @@ devnet = ChainwebVersion , _versionCheats = Cheats { _disablePeerValidation = True , _disablePow = False + , _fakeFirstEpochStart = True , _disablePact = False - , _disableMempool = False + , _disableMempoolSync = False } } diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 1a31127d96..08d135b6d9 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -143,12 +143,11 @@ mainnet = ChainwebVersion (to20ChainsMainnet, twentyChainGraph) `Above` End petersonChainGraph , _versionBlockRate = BlockRate 30_000_000 - , _versionWindow = Just $ WindowWidth 120 + , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` End Nothing - , _versionFakeFirstEpochStart = False , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts , _versionGenesis = ChainwebGenesis { _genesisBlockTarget = OnChains $ HM.fromList $ concat @@ -193,7 +192,8 @@ mainnet = ChainwebVersion , _versionCheats = Cheats { _disablePeerValidation = False , _disablePow = False - , _disableMempool = False + , _fakeFirstEpochStart = False + , _disableMempoolSync = False , _disablePact = False } } diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index 46626460a4..ea8df87c4e 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -123,12 +123,11 @@ testnet = ChainwebVersion (to20ChainsTestnet, twentyChainGraph) `Above` End petersonChainGraph , _versionBlockRate = BlockRate 30_000_000 - , _versionWindow = Just $ WindowWidth 120 + , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = (succ $ testnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` End Nothing - , _versionFakeFirstEpochStart = False , _versionBootstraps = domainAddr2PeerInfo testnetBootstrapHosts , _versionGenesis = ChainwebGenesis { _genesisBlockTarget = OnChains $ HM.fromList $ concat @@ -164,7 +163,8 @@ testnet = ChainwebVersion , _versionCheats = Cheats { _disablePeerValidation = False , _disablePow = False - , _disableMempool = False + , _fakeFirstEpochStart = False + , _disableMempoolSync = False , _disablePact = False } } diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 5c226833bd..ea8f80e6e0 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -245,7 +245,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(enforce false 'hi)") $ assertTxFailure "Should fail with the error from the enforce" "hi" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce pre-fork evaluates the string with gas" 34 + assertTxGas "Enforce pre-fork evaluates the string with gas" 35 , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions pre-fork gas" 20 , PactTxTest @@ -259,7 +259,7 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(+ 1 \'clearlyanerror)") $ assertTxFailure "Should replace tx error with empty error" "" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce post fork does not eval the string" (14 + coinTxBuyTransferGas) + assertTxGas "Enforce post fork does not eval the string" (15 + coinTxBuyTransferGas) , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ assertTxGas "List functions post-fork change gas" (40 + coinTxBuyTransferGas) , PactTxTest @@ -657,7 +657,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Pre-fork format gas" 21 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Pre-fork try" 18 + assertTxGas "Pre-fork try" 19 , PactTxTest (buildSimpleCmd defineNonNamespacedPreFork) $ assertTxSuccess "Should pass when defining a non-namespaced keyset" @@ -673,7 +673,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Post-fork format gas increase" 48 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Post-fork try should charge a bit more gas" 19 + assertTxGas "Post-fork try should charge a bit more gas" 20 , PactTxTest (buildSimpleCmd defineNonNamespacedPostFork1) $ assertTxFailure "Should fail when defining a non-namespaced keyset post fork" @@ -877,7 +877,7 @@ pact4coin3UpgradeTest = do "coin" v3Hash assertTxEvents "Events for tx1 @ block 22" [gasEv1,allocEv,allocTfr] cr , PactTxTest (buildXSend []) $ \cr -> do - gasEv2 <- mkTransferEvent "sender00" "NoMiner" 0.0014 "coin" v3Hash + gasEv2 <- mkTransferEvent "sender00" "NoMiner" 0.0015 "coin" v3Hash sendTfr <- mkTransferEvent "sender00" "" 0.0123 "coin" v3Hash yieldEv <- mkXYieldEvent "sender00" "sender00" 0.0123 sender00Ks "pact" v3Hash "0" "0" assertTxEvents "Events for tx2 @ block 22" [gasEv2,sendTfr, yieldEv] cr diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 5c5a22b337..9841661139 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -101,8 +101,7 @@ testVersionTemplate :: VersionBuilder testVersionTemplate v = v & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + 0x80000000) & versionHeaderBaseSizeBytes .~ 318 - 110 - & versionWindow .~ Nothing - & versionFakeFirstEpochStart .~ False -- DA is already disabled with _versionWindow = Nothing + & versionWindow .~ WindowWidth 120 & versionMaxBlockGasLimit .~ End (Just 2_000_000) & versionBootstraps .~ [testBootstrapPeerInfos] @@ -138,14 +137,15 @@ barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) barebonesTestVersion' :: ChainGraph -> VersionBuilder barebonesTestVersion' g v = testVersionTemplate v - & versionWindow .~ Nothing - & versionBlockRate .~ BlockRate 0 + & versionWindow .~ WindowWidth 120 + & versionBlockRate .~ BlockRate 1_000_000 & versionName .~ ChainwebVersionName ("test-" <> toText g) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing + { _disablePow = True + , _fakeFirstEpochStart = True , _disablePact = True - , _disableMempool = True + , _disableMempoolSync = True , _disablePeerValidation = True } & versionGenesis .~ ChainwebGenesis @@ -163,13 +163,14 @@ barebonesTestVersion' g v = cpmTestVersion :: ChainGraph -> VersionBuilder cpmTestVersion g v = v - & versionWindow .~ Nothing + & versionWindow .~ WindowWidth 120 & versionBlockRate .~ BlockRate (Micros 100_000) & versionGraphs .~ End g & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing + { _disablePow = True + , _fakeFirstEpochStart = True , _disablePact = False - , _disableMempool = False + , _disableMempoolSync = False , _disablePeerValidation = True } & versionGenesis .~ ChainwebGenesis @@ -247,19 +248,20 @@ timedConsensusVersion' g1 g2 v = testVersionTemplate v & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) & versionBlockRate .~ BlockRate 1_000_000 - & versionWindow .~ Nothing + & versionWindow .~ WindowWidth 120 & versionForks .~ tabulateHashMap (\case SkipTxTimingValidation -> AllChains (BlockHeight 2) -- pact is disabled, we don't care about pact forks _ -> AllChains (BlockHeight 0) ) & versionUpgrades .~ AllChains HM.empty - & versionWindow .~ Nothing + & versionWindow .~ WindowWidth 120 & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) & versionCheats .~ Cheats - { _disablePow = False -- PoW is effectively disabled with _versionWindow = Nothing + { _disablePow = True + , _fakeFirstEpochStart = True , _disablePact = True - , _disableMempool = True + , _disableMempoolSync = True , _disablePeerValidation = True } & versionGenesis .~ ChainwebGenesis From b23d19881360c3402fd4ae6e05b0ecaa9303ea37 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sun, 5 Mar 2023 16:24:36 -0500 Subject: [PATCH 32/91] self-review --- chainweb.cabal | 1 - src/Chainweb/Rosetta/Internal.hs | 15 +-- src/Chainweb/Rosetta/RestAPI/Server.hs | 1 - src/Chainweb/Version/Utils.hs | 9 +- src/Chainweb/WebBlockHeaderDB.hs | 3 +- src/P2P/Node.hs | 1 - test/Chainweb/Test/Cut.hs | 2 +- test/Chainweb/Test/Mining.hs | 2 +- test/Chainweb/Test/Orphans/Internal.hs | 20 +-- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 2 +- test/Chainweb/Test/Pact/RemotePactTest.hs | 66 +++++----- test/Chainweb/Test/Pact/TransactionTests.hs | 6 +- test/Chainweb/Test/Pact/Utils.hs | 11 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 7 +- test/Chainweb/Test/SPV.hs | 21 ++-- test/Chainweb/Test/Utils/ApiQueries.hs | 119 ------------------ test/Chainweb/Test/Utils/TestHeader.hs | 53 -------- test/Chainweb/Test/Version.hs | 4 - 18 files changed, 75 insertions(+), 268 deletions(-) delete mode 100644 test/Chainweb/Test/Utils/ApiQueries.hs diff --git a/chainweb.cabal b/chainweb.cabal index 770be42192..aa0b062b6f 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -489,7 +489,6 @@ test-suite chainweb-tests Chainweb.Test.Version Chainweb.Test.Utils.BlockHeader Chainweb.Test.Utils.TestHeader - -- Chainweb.Test.Utils.ApiQueries -- Data Data.Test.PQueue diff --git a/src/Chainweb/Rosetta/Internal.hs b/src/Chainweb/Rosetta/Internal.hs index 9919a3346b..68e901256f 100644 --- a/src/Chainweb/Rosetta/Internal.hs +++ b/src/Chainweb/Rosetta/Internal.hs @@ -56,7 +56,6 @@ import Chainweb.BlockHeader import Chainweb.ChainId import Chainweb.Cut import Chainweb.CutDB --- import Chainweb.Pact.Transactions.UpgradeTransactions import Chainweb.Pact.Service.Types (Domain'(..), BlockTxHistory(..)) import Chainweb.Payload hiding (Transaction(..)) import Chainweb.Payload.PayloadStore @@ -743,7 +742,7 @@ toSignerAcctsMap txInfo payerAcct cid pacts cutDb = do someActualFrom <- getOwnership peCurr bhCurr from someActualTo <- getOwnership peCurr bhCurr to - _ <- enforceAcctPresent' from someActualFrom + _ <- enforceAcctPresent from someActualFrom checkExpectedOwnership from expectedFrom someActualFrom checkExpectedOwnership to expectedTo someActualTo @@ -817,18 +816,6 @@ enforceAcctPresent k actualOwnership = stringRosettaError RosettaInvalidAccountProvided $ "Account=" ++ show k ++ " doesn't exists" -enforceAcctPresent' - :: AccountId - -> Maybe [T.Text] - -> ExceptT RosettaError Handler [T.Text] -enforceAcctPresent' k actualOwnership = - case actualOwnership of - Just pks -> pure pks - Nothing -> -- key missing (not expected) - hoistEither $ Left $ - stringRosettaError RosettaInvalidAccountProvided $ - "Account=" ++ show k ++ " doesn't exists (2)" - checkExpectedOwnership :: AccountId -> [T.Text] diff --git a/src/Chainweb/Rosetta/RestAPI/Server.hs b/src/Chainweb/Rosetta/RestAPI/Server.hs index 0d28ac8b96..0a23579a50 100644 --- a/src/Chainweb/Rosetta/RestAPI/Server.hs +++ b/src/Chainweb/Rosetta/RestAPI/Server.hs @@ -6,7 +6,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeApplications #-} -{-# LANGUAGE ViewPatterns #-} -- | -- Module: Chainweb.Rosetta.RestAPI.Server diff --git a/src/Chainweb/Version/Utils.hs b/src/Chainweb/Version/Utils.hs index 066cf84d4f..cf24fce406 100644 --- a/src/Chainweb/Version/Utils.hs +++ b/src/Chainweb/Version/Utils.hs @@ -4,6 +4,7 @@ {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ViewPatterns #-} -- | -- Module: Chainweb.Version.Utils @@ -78,6 +79,7 @@ import Chainweb.Graph import Chainweb.Utils import Chainweb.Utils.Rule import Chainweb.Version +import Chainweb.Version.Mainnet -- -------------------------------------------------------------------------- -- -- Utils @@ -116,7 +118,12 @@ atCutHeight h = snd . fromJuste . M.lookupLE h -- @ -- chainGraphs :: HasChainwebVersion v => v -> M.Map BlockHeight ChainGraph -chainGraphs v = M.fromDistinctDescList . toList . ruleElems minBound $ _versionGraphs $ _chainwebVersion v +chainGraphs = \case + (_chainwebVersion -> v) + | _versionCode v == _versionCode mainnet -> mainnetGraphs + | otherwise -> M.fromDistinctDescList . toList . ruleElems minBound $ _versionGraphs v + where + mainnetGraphs = M.fromDistinctDescList . toList . ruleElems minBound $ _versionGraphs mainnet -- | BlockHeight intervals for the chain graphs of a chainweb version up to a -- given block height. diff --git a/src/Chainweb/WebBlockHeaderDB.hs b/src/Chainweb/WebBlockHeaderDB.hs index 337e8478b2..be204c996c 100644 --- a/src/Chainweb/WebBlockHeaderDB.hs +++ b/src/Chainweb/WebBlockHeaderDB.hs @@ -48,7 +48,6 @@ import Data.Functor.Of import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.List as L -import GHC.Stack import qualified Streaming.Prelude as S @@ -123,7 +122,7 @@ instance (k ~ CasKeyType (ChainValue BlockHeader)) => ReadableTable WebBlockHead {-# INLINE tableLookup #-} initWebBlockHeaderDb - :: HasCallStack => RocksDb + :: RocksDb -> ChainwebVersion -> IO WebBlockHeaderDb initWebBlockHeaderDb db v = WebBlockHeaderDb diff --git a/src/P2P/Node.hs b/src/P2P/Node.hs index f3040c5448..c197e52ec5 100644 --- a/src/P2P/Node.hs +++ b/src/P2P/Node.hs @@ -10,7 +10,6 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} -{-# LANGUAGE TypeFamilies #-} -- | -- Module: P2P.Node diff --git a/test/Chainweb/Test/Cut.hs b/test/Chainweb/Test/Cut.hs index 3d7320d944..0ce435d449 100644 --- a/test/Chainweb/Test/Cut.hs +++ b/test/Chainweb/Test/Cut.hs @@ -655,7 +655,7 @@ properties db <> properties_miscCut db v <> properties_misc where - v = barebonesTestVersion pairChainGraph + v = barebonesTestVersion pairChainGraph -- -------------------------------------------------------------------------- -- -- TestTools diff --git a/test/Chainweb/Test/Mining.hs b/test/Chainweb/Test/Mining.hs index 80ce82dbf4..afb9864730 100644 --- a/test/Chainweb/Test/Mining.hs +++ b/test/Chainweb/Test/Mining.hs @@ -67,7 +67,6 @@ withTestCoordinator -> (forall tbl logger . Logger logger => logger -> MiningCoordination logger tbl -> IO ()) -> IO () withTestCoordinator rdb maybeConf a = do - let v = barebonesTestVersion pairChainGraph var <- newEmptyMVar x <- race (takeMVar var) $ withTestCutDb rdb v id 0 (\_ _ -> return fakePact) (logFunction logger) $ \_ cdb -> @@ -81,6 +80,7 @@ withTestCoordinator rdb maybeConf a = do Right () -> logFunctionText logger Info "withTestCoordinator: coordinator service stopped" where + v = barebonesTestVersion pairChainGraph logger = genericLogger Warn print conf = fromMaybe defaultMining maybeConf & miningCoordination . coordinationEnabled .~ True diff --git a/test/Chainweb/Test/Orphans/Internal.hs b/test/Chainweb/Test/Orphans/Internal.hs index fa3ba27d6c..bbb660397c 100644 --- a/test/Chainweb/Test/Orphans/Internal.hs +++ b/test/Chainweb/Test/Orphans/Internal.hs @@ -140,6 +140,7 @@ import Chainweb.SPV.OutputProof import Chainweb.SPV.PayloadProof import Chainweb.Test.Orphans.Pact import Chainweb.Test.Orphans.Time () +import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Utils import Chainweb.Utils.Paging @@ -147,6 +148,7 @@ import Chainweb.Utils.Serialization import Chainweb.Version import Chainweb.Version.Development import Chainweb.Version.Mainnet +import Chainweb.Version.Registry import Chainweb.Version.Testnet import Chainweb.Version.Utils @@ -183,15 +185,13 @@ instance Arbitrary Utf8Encoded where instance Arbitrary ChainwebVersion where arbitrary = elements - -- [ Test singletonChainGraph - -- , Test petersonChainGraph - -- , TimedConsensus singletonChainGraph singletonChainGraph - -- , TimedConsensus petersonChainGraph petersonChainGraph - -- , TimedConsensus singletonChainGraph pairChainGraph - -- , TimedConsensus petersonChainGraph twentyChainGraph - -- , PowConsensus singletonChainGraph - -- , PowConsensus petersonChainGraph - [ Development + [ barebonesTestVersion singletonChainGraph + , barebonesTestVersion petersonChainGraph + , timedConsensusVersion singletonChainGraph singletonChainGraph + , timedConsensusVersion petersonChainGraph petersonChainGraph + , timedConsensusVersion singletonChainGraph pairChainGraph + , timedConsensusVersion petersonChainGraph twentyChainGraph + , Development , Testnet04 , Mainnet01 ] @@ -209,7 +209,7 @@ instance MerkleHashAlgorithm a => Arbitrary (MerkleLogHash a) where -- A somewhat boring instance. Mostly the default value. -- instance Arbitrary ChainwebConfiguration where - arbitrary = defaultChainwebConfiguration <$> arbitrary + arbitrary = defaultChainwebConfiguration <$> elements knownVersions -- -------------------------------------------------------------------------- -- -- POW diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index ea8f80e6e0..f5716a23ff 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -131,7 +131,7 @@ tests = ScheduledTest testName go where -- This is way more than what is used in production, but during testing -- we can be generous. - generousConfig = defaultPactServiceConfig { _pactBlockGasLimit = 300_000, _pactLogGas = True } + generousConfig = defaultPactServiceConfig { _pactBlockGasLimit = 300_000 } timeoutConfig = defaultPactServiceConfig { _pactBlockGasLimit = 100_000 } test pactConfig gasmodel tname f = withDelegateMempool $ \dmpio -> testCase tname $ diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 85acb8b4f5..b49e73c3c0 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -122,40 +122,40 @@ gp = 0.1 tests :: RocksDb -> ScheduledTest tests rdb = testGroupSch "Chainweb.Test.Pact.RemotePactTest" [ withNodesAtLatestBehavior v "remotePactTest-" rdb nNodes $ \net -> do - withMVarResource 0 $ \iomvar -> + withMVarResource 0 $ \iomvar -> withTime $ \iot -> - testGroup "remote pact tests" - [ withRequestKeys iot iomvar net $ responseGolden net - , after AllSucceed "remote-golden" $ - testGroup "remote spv" [spvTest iot net] - , after AllSucceed "remote-golden" $ - testGroup "remote eth spv" [ethSpvTest iot net] - , after AllSucceed "remote spv" $ - sendValidationTest iot net - , after AllSucceed "remote spv" $ - pollingBadlistTest net - , after AllSucceed "remote spv" $ - testCase "trivialLocalCheck" $ - localTest iot net - , after AllSucceed "remote spv" $ - testCase "localChainData" $ - localChainDataTest iot net - , after AllSucceed "remote spv" $ - testGroup "gasForTxSize" - [ txTooBigGasTest iot net ] - , after AllSucceed "remote spv" $ - testGroup "genesisAllocations" - [ allocationTest iot net ] - , after AllSucceed "genesisAllocations" $ - testGroup "caplistTests" - [ caplistTest iot net ] - , after AllSucceed "caplistTests" $ - localContTest iot net - , after AllSucceed "local continuation test" $ - pollBadKeyTest net - , after AllSucceed "poll bad key test" $ - localPreflightSimTest iot net - ] + testGroup "remote pact tests" + [ withRequestKeys iot iomvar net $ responseGolden net + , after AllSucceed "remote-golden" $ + testGroup "remote spv" [spvTest iot net] + , after AllSucceed "remote-golden" $ + testGroup "remote eth spv" [ethSpvTest iot net] + , after AllSucceed "remote spv" $ + sendValidationTest iot net + , after AllSucceed "remote spv" $ + pollingBadlistTest net + , after AllSucceed "remote spv" $ + testCase "trivialLocalCheck" $ + localTest iot net + , after AllSucceed "remote spv" $ + testCase "localChainData" $ + localChainDataTest iot net + , after AllSucceed "remote spv" $ + testGroup "gasForTxSize" + [ txTooBigGasTest iot net ] + , after AllSucceed "remote spv" $ + testGroup "genesisAllocations" + [ allocationTest iot net ] + , after AllSucceed "genesisAllocations" $ + testGroup "caplistTests" + [ caplistTest iot net ] + , after AllSucceed "caplistTests" $ + localContTest iot net + , after AllSucceed "local continuation test" $ + pollBadKeyTest net + , after AllSucceed "poll bad key test" $ + localPreflightSimTest iot net + ] ] responseGolden :: IO ChainwebNetwork -> IO RequestKeys -> TestTree diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 7206d3d774..890c2d2eda 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -318,10 +318,10 @@ testCoinbaseUpgradeDevnet cid upgradeHeight = testTwentyChainDevnetUpgrades :: TestTree testTwentyChainDevnetUpgrades = testCaseSteps "Test 20-chain Devnet upgrades" $ \step -> do - step "Check that 20-chain upgrades fire at block height 150" + step "Check that 20-chain upgrades fire at block height 12" testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) 12 test0 - step "Check that 20-chain upgrades do not fire at block heights < 150 and > 150" + step "Check that 20-chain upgrades do not fire at block heights < 12 and > 12" testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (12 - 1) test1 testUpgradeScript "test/pact/twenty-chain-upgrades.repl" (unsafeChainId 0) (12 + 1) test1 @@ -366,7 +366,7 @@ testUpgradeScript script cid bh test = do where p = parent bh cid -matchLogs :: HasCallStack => [(Text, Text, Maybe Value)] -> [(Text, Text, Maybe Value)] -> IO () +matchLogs :: [(Text, Text, Maybe Value)] -> [(Text, Text, Maybe Value)] -> IO () matchLogs expectedResults actualResults | length actualResults /= length expectedResults = void $ assertFailure $ intercalate "\n" $ diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index c14caf0946..3a42856223 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -140,7 +140,6 @@ import Data.String import qualified Data.Vector as V import GHC.Generics -import GHC.Stack import System.Directory import System.IO.Temp (createTempDirectory) @@ -200,7 +199,7 @@ import Chainweb.Test.TestVersions import Chainweb.Time import Chainweb.Transaction import Chainweb.Utils -import Chainweb.Version +import Chainweb.Version (ChainwebVersion(..), chainIds) import qualified Chainweb.Version as Version import Chainweb.Version.Utils (someChainId) import Chainweb.WebBlockHeaderDB @@ -612,7 +611,7 @@ testPactCtxSQLite v cid bhdb pdb sqlenv conf gasmodel = do !ctx <- TestPactCtx <$!> newMVar (PactServiceState Nothing mempty ph noSPVSupport) <*> pure (pactServiceEnv cpe rs) - evalPactServiceM_ ctx (initialPayloadState (genericLogger Info T.putStrLn) mempty v cid) + evalPactServiceM_ ctx (initialPayloadState dummyLogger mempty v cid) return (ctx, PactDbEnv' dbSt) where initialBlockState = initBlockState defaultModuleCacheLimit $ genesisHeight v cid @@ -815,8 +814,7 @@ withTemporaryDir = withResource removeDirectoryRecursive withTestBlockDbTest - :: HasCallStack - => ChainwebVersion + :: ChainwebVersion -> RocksDb -> (IO TestBlockDb -> TestTree) -> TestTree @@ -824,8 +822,7 @@ withTestBlockDbTest v rdb = withResource (mkTestBlockDb v rdb) mempty -- | Single-chain Pact via service queue. withPactTestBlockDb - :: HasCallStack - => ChainwebVersion + :: ChainwebVersion -> ChainId -> LogLevel -> RocksDb diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index e518d8f81c..a5d4faedc2 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -162,7 +162,6 @@ accountBalanceTests tio envIo = where req = AccountBalanceReq nid (AccountId "sender00" Nothing Nothing) Nothing - checkBalance :: HasCallStack => AccountBalanceResp -> Decimal -> IO () checkBalance resp bal1 = do let b0 = head $ _accountBalanceResp_balances resp b1 = kdaToRosettaAmount bal1 @@ -502,8 +501,7 @@ constructionTransferTests _ envIo = getKeys _ = Nothing submitToConstructionAPI - :: HasCallStack - => [Operation] + :: [Operation] -> ChainId -> Text -> (Text -> Maybe SimpleKeyPair) @@ -752,8 +750,7 @@ operationTypes = -- | Validate all useful data for a tx operation -- validateOp - :: HasCallStack - => Word64 + :: Word64 -- ^ op idx -> Text -- ^ operation type diff --git a/test/Chainweb/Test/SPV.hs b/test/Chainweb/Test/SPV.hs index 775c5b1a49..5880643f0d 100644 --- a/test/Chainweb/Test/SPV.hs +++ b/test/Chainweb/Test/SPV.hs @@ -83,9 +83,6 @@ import Chainweb.Version import Chainweb.Storage.Table import Chainweb.Storage.Table.RocksDB -version :: ChainwebVersion -version = barebonesTestVersion petersonChainGraph - -- -------------------------------------------------------------------------- -- -- Test Tree @@ -93,14 +90,16 @@ version = barebonesTestVersion petersonChainGraph -- quickCheck instead of HUnit or should be derandomized. -- tests :: RocksDb -> TestTree -tests rdb = - testGroup "SPV tests" - [ testCaseStepsN "SPV transaction proof" 10 (spvTransactionRoundtripTest rdb version) - , testCaseStepsN "SPV transaction output proof" 10 (spvTransactionOutputRoundtripTest rdb version) - , apiTests rdb version - , testCaseSteps "SPV transaction proof test" (spvTest rdb version) - , properties - ] +tests rdb =testGroup "SPV tests" + [ testCaseStepsN "SPV transaction proof" 10 (spvTransactionRoundtripTest rdb version) + , testCaseStepsN "SPV transaction output proof" 10 (spvTransactionOutputRoundtripTest rdb version) + , apiTests rdb version + , testCaseSteps "SPV transaction proof test" (spvTest rdb version) + , properties + ] + where + version = barebonesTestVersion petersonChainGraph + -- -------------------------------------------------------------------------- -- -- Utils diff --git a/test/Chainweb/Test/Utils/ApiQueries.hs b/test/Chainweb/Test/Utils/ApiQueries.hs deleted file mode 100644 index 4bf1ce78e9..0000000000 --- a/test/Chainweb/Test/Utils/ApiQueries.hs +++ /dev/null @@ -1,119 +0,0 @@ -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} - --- | --- Module: Chainweb.Test.Utils.ApiQueries --- Copyright: Copyright © 2020 Kadena LLC. --- License: MIT --- Maintainer: Lars Kuhtz --- Stability: experimental --- --- TODO --- -module Chainweb.Test.Utils.ApiQueries -( mkMgr -, runQuery -, getHeaderByHash -, getHeaderByHeight -) where - -import Chainweb.BlockHash -import Chainweb.TreeDB hiding (lookup) - -import Control.Lens hiding ((.=)) -import Control.Monad - -import qualified Data.HashSet as HS - -import GHC.Stack - -import qualified Network.Connection as HTTP -import qualified Network.HTTP.Client as HTTP -import qualified Network.HTTP.Client.TLS as HTTP - -import qualified Servant.Client as HTTP - --- internal modules - -import Chainweb.BlockHeader -import Chainweb.BlockHeaderDB.RestAPI.Client -import Chainweb.BlockHeight -import Chainweb.Cut.CutHashes -import Chainweb.CutDB.RestAPI.Client -import Chainweb.Utils -import Chainweb.Utils.Paging -import Chainweb.Version - --- -------------------------------------------------------------------------- -- --- Endpoints - -endpoint :: HasCallStack => HTTP.Manager -> ChainwebVersionName -> HTTP.ClientEnv -endpoint mgr Mainnet01 = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us-e1.chainweb.com" 443 "" -endpoint mgr Testnet04 = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us1.testnet.chainweb.com" 443 "" -endpoint mgr (Development ()) = HTTP.mkClientEnv mgr $ HTTP.BaseUrl HTTP.Https "us1.tn.chainweb.com" 443 "" -endpoint _ x = error $ "endpoint: unsupported chainweb version " <> sshow x - --- -------------------------------------------------------------------------- -- --- Tools - -mkMgr :: IO HTTP.Manager -mkMgr = HTTP.newTlsManagerWith $ HTTP.mkManagerSettings - (HTTP.TLSSettingsSimple True True True) - Nothing - -runQuery - :: HasCallStack - => HTTP.Manager - -> ChainwebVersionTag - -> HTTP.ClientM a - -> IO a -runQuery mgr v q = HTTP.runClientM q (endpoint mgr v) >>= \case - Left e -> error (show e) - Right x -> return x - --- -------------------------------------------------------------------------- -- --- API Queries - -getHeaderByHash - :: HasCallStack - => HTTP.Manager - -> ChainwebVersionTag - -> ChainId - -> BlockHash - -> IO BlockHeader -getHeaderByHash mgr v c = runQuery mgr v . headerClient v c - -getHeaderByHeight - :: HasCallStack - => HTTP.Manager - -> ChainwebVersionTag - -> ChainId - -> BlockHeight - -> IO BlockHeader -getHeaderByHeight mgr v cid height = do - BlockHashWithHeight curHeight curHash <- currentHash mgr v cid - when (curHeight < height) $ - error $ "getHeaderByHeight: height " <> sshow height <> " is in the future. Current height " <> sshow curHeight - page <- runQuery mgr v $ query curHash - when (_pageLimit page < 1) $ - error $ "getHeaderByHeight: no header found for height " <> sshow height <> " on chain " <> sshow cid - when (_pageLimit page > 1) $ - error $ "getHeaderByHeight: expect just one result but got " <> sshow (_pageLimit page) - return $ head $ _pageItems page - where - query b = branchHeadersClient v cid (Just 1) Nothing - Nothing - (Just $ int height) - (BranchBounds mempty $ HS.singleton (UpperBound b)) - -currentHash - :: HasCallStack - => HTTP.Manager - -> ChainwebVersionTag - -> ChainId - -> IO BlockHashWithHeight -currentHash mgr v cid = do - c <- runQuery mgr v $ cutGetClient v - return $ c ^?! cutHashes . ix cid - diff --git a/test/Chainweb/Test/Utils/TestHeader.hs b/test/Chainweb/Test/Utils/TestHeader.hs index d76dca20dd..85f685e66f 100644 --- a/test/Chainweb/Test/Utils/TestHeader.hs +++ b/test/Chainweb/Test/Utils/TestHeader.hs @@ -57,7 +57,6 @@ import Chainweb.BlockHeader import Chainweb.BlockHeight import Chainweb.ChainValue import Chainweb.Test.Orphans.Internal --- import Chainweb.Test.Utils.ApiQueries import Chainweb.Version import Chainweb.Storage.Table @@ -202,55 +201,3 @@ genesisTestHeader v cid = TestHeader } where gen = genesisBlockHeader (_chainwebVersion v) (_chainId cid) - --- -- -------------------------------------------------------------------------- -- --- -- Query TestHeader from a network - --- queryTestHeader --- :: HasCallStack --- => HasChainwebVersion v --- => HasChainId c --- => v --- -> c --- -> BlockHash --- -> IO TestHeader --- queryTestHeader v c h = do --- mgr <- mkMgr --- hdr <- getHeaderByHash mgr (chainwebVersionTag ver) cid h --- parent <- getHeaderByHash mgr (chainwebVersionTag ver) cid $ _blockParent hdr --- ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr (chainwebVersionTag ver) ac a) --- $ _getBlockHashRecord --- $ _blockAdjacentHashes hdr --- return $ TestHeader --- { _testHeaderHdr = hdr --- , _testHeaderParent = ParentHeader parent --- , _testHeaderAdjs = toList ads --- } --- where --- ver = _chainwebVersion v --- cid = _chainId c - --- queryTestHeaderByHeight --- :: HasCallStack --- => HasChainwebVersion v --- => HasChainId c --- => v --- -> c --- -> BlockHeight --- -> IO TestHeader --- queryTestHeaderByHeight v c h = do --- mgr <- mkMgr --- hdr <- getHeaderByHeight mgr ver cid h --- parent <- getHeaderByHash mgr ver cid $ _blockParent hdr --- ads <- itraverse (\ac a -> ParentHeader <$> getHeaderByHash mgr ver ac a) --- $ _getBlockHashRecord --- $ _blockAdjacentHashes hdr --- return $ TestHeader --- { _testHeaderHdr = hdr --- , _testHeaderParent = ParentHeader parent --- , _testHeaderAdjs = toList ads --- } --- where --- ver = _chainwebVersion v --- cid = _chainId c - diff --git a/test/Chainweb/Test/Version.hs b/test/Chainweb/Test/Version.hs index bc49d51f5b..fdadf0735a 100644 --- a/test/Chainweb/Test/Version.hs +++ b/test/Chainweb/Test/Version.hs @@ -1,10 +1,6 @@ -{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -Wno-missing-fields #-} -- | -- Module: Chainweb.Test.Version From 48f6f096c7b2c5b7df143880f25c28b99c43baa8 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Sun, 5 Mar 2023 17:06:52 -0500 Subject: [PATCH 33/91] More self-review --- chainweb.cabal | 1 - src/Chainweb/BlockHeader.hs | 26 ++++++++++++++++-- src/Chainweb/BlockHeader/Validation.hs | 1 - src/Chainweb/Cut/Create.hs | 2 -- src/Chainweb/CutDB.hs | 2 +- src/Chainweb/Difficulty.hs | 6 ++-- src/Chainweb/Mempool/Consensus.hs | 3 +- src/Chainweb/Miner/RestAPI/Server.hs | 1 - src/Chainweb/Pact/SPV.hs | 1 - src/Chainweb/Pact/TransactionExec.hs | 38 -------------------------- 10 files changed, 28 insertions(+), 53 deletions(-) diff --git a/chainweb.cabal b/chainweb.cabal index aa0b062b6f..e9647c47bd 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -308,7 +308,6 @@ library , Chainweb.Pact.Transactions.Mainnet9Transactions , Chainweb.Pact.Transactions.MainnetKADTransactions , Chainweb.Pact.Transactions.OtherTransactions - -- , Chainweb.Pact.Transactions.UpgradeTransactions , Chainweb.Pact.Types , Chainweb.Pact.Utils , Chainweb.Pact.Validations diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 400a6eef13..67337d4f0d 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -190,6 +190,8 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag Nonce where type Tag Nonce = 'BlockNonceTag toMerkleNode = encodeMerkleInputNode encodeNonce fromMerkleNode = decodeMerkleInputNode decodeNonce + {-# INLINE toMerkleNode #-} + {-# INLINE fromMerkleNode #-} encodeNonce :: Nonce -> Put encodeNonce (Nonce n) = putWord64le n @@ -203,6 +205,8 @@ decodeNonce = Nonce <$> getWord64le instance ToJSON Nonce where toJSON (Nonce i) = toJSON $ show i toEncoding (Nonce i) = toEncoding $ show i + {-# INLINE toJSON #-} + {-# INLINE toEncoding #-} instance FromJSON Nonce where parseJSON = withText "Nonce" @@ -220,6 +224,8 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag EpochStartT type Tag EpochStartTime = 'EpochStartTimeTag toMerkleNode = encodeMerkleInputNode encodeEpochStartTime fromMerkleNode = decodeMerkleInputNode decodeEpochStartTime + {-# INLINE toMerkleNode #-} + {-# INLINE fromMerkleNode #-} encodeEpochStartTime :: EpochStartTime -> Put encodeEpochStartTime (EpochStartTime t) = encodeTime t @@ -358,6 +364,7 @@ data BlockHeader :: Type where instance Eq BlockHeader where (==) = (==) `on` _blockHash + {-# INLINE (==) #-} instance Ord BlockHeader where compare = compare `on` _blockHash @@ -377,6 +384,7 @@ instance HasChainwebVersion BlockHeader where instance IsCasValue BlockHeader where type CasKeyType BlockHeader = BlockHash casKey = _blockHash + {-# INLINE casKey #-} type BlockHeaderCas tbl = Cas tbl BlockHeader @@ -414,11 +422,11 @@ slowEpoch :: ParentHeader -> BlockCreationTime -> Bool slowEpoch (ParentHeader p) (BlockCreationTime ct) = actual > (expected * 5) where EpochStartTime es = _blockEpochStart p - BlockRate s = _versionBlockRate (_blockChainwebVersion p) + BlockRate br = _versionBlockRate (_blockChainwebVersion p) WindowWidth ww = _versionWindow (_blockChainwebVersion p) expected :: Micros - expected = s * int ww + expected = br * int ww actual :: Micros actual = timeSpanToMicros $ ct .-. es @@ -472,6 +480,7 @@ powTarget p@(ParentHeader ph) as bct = case effectiveWindow ph of avgTarget targets = HashTarget $ floor $ s / int (length targets) where s = sum $ fmap (int @_ @Rational . _hashTarget) targets +{-# INLINE powTarget #-} -- | Compute the epoch start value for a new BlockHeader -- @@ -577,6 +586,7 @@ epochStart ph@(ParentHeader p) adj (BlockCreationTime bt) parentIsFirstOnNewChain = _blockHeight p > 1 && _blockHeight p == genesisHeight ver cid + 1 +{-# INLINE epochStart #-} -- -------------------------------------------------------------------------- -- -- Newtype wrappers for function parameters @@ -597,12 +607,15 @@ parentHeader = lens _parentHeader $ \_ hdr -> ParentHeader hdr instance HasChainId ParentHeader where _chainId = _chainId . _parentHeader + {-# INLINE _chainId #-} instance HasChainwebVersion ParentHeader where _chainwebVersion = _chainwebVersion . _parentHeader + {-# INLINE _chainwebVersion #-} instance HasChainGraph ParentHeader where _chainGraph = _chainGraph . _parentHeader + {-# INLINE _chainGraph #-} isGenesisBlockHeader :: BlockHeader -> Bool isGenesisBlockHeader b = @@ -908,6 +921,8 @@ decodeBlockHeader = BlockHeader instance ToJSON BlockHeader where toJSON = toJSON . encodeB64UrlNoPaddingText . runPutS . encodeBlockHeader toEncoding = b64UrlNoPaddingTextEncoding . runPutS . encodeBlockHeader + {-# INLINE toJSON #-} + {-# INLINE toEncoding #-} instance FromJSON BlockHeader where parseJSON = withText "BlockHeader" $ \t -> @@ -931,9 +946,11 @@ getAdjacentHash p b = firstOf (blockAdjacentHashes . ixg (_chainId p)) b ??? ChainNotAdjacentException (Expected $ _chainId p) (Actual $ _blockAdjacentChainIds b) +{-# INLINE getAdjacentHash #-} computeBlockHash :: BlockHeader -> BlockHash computeBlockHash h = BlockHash $ MerkleLogHash $ computeMerkleLogRoot h +{-# INLINE computeBlockHash #-} -- | The Proof-Of-Work hash includes all data in the block except for the -- '_blockHash'. The value (interpreted as 'BlockHashNat' must be smaller than @@ -945,6 +962,7 @@ _blockPow h = cryptoHash @Blake2s_256 blockPow :: Getter BlockHeader PowHash blockPow = to _blockPow +{-# INLINE blockPow #-} -- | The number of microseconds between the creation time of two `BlockHeader`s. -- @@ -991,10 +1009,13 @@ blockHeaderProperties (ObjectEncoded b) = , "featureFlags" .= _blockFlags b , "hash" .= _blockHash b ] +{-# INLINE blockHeaderProperties #-} instance ToJSON (ObjectEncoded BlockHeader) where toJSON = object . blockHeaderProperties toEncoding = pairs . mconcat . blockHeaderProperties + {-# INLINE toJSON #-} + {-# INLINE toEncoding #-} parseBlockHeaderObject :: Object -> Parser BlockHeader parseBlockHeaderObject o = BlockHeader @@ -1015,6 +1036,7 @@ parseBlockHeaderObject o = BlockHeader instance FromJSON (ObjectEncoded BlockHeader) where parseJSON = withObject "BlockHeader" $ fmap ObjectEncoded . parseBlockHeaderObject + {-# INLINE parseJSON #-} -- -------------------------------------------------------------------------- -- -- IsBlockHeader diff --git a/src/Chainweb/BlockHeader/Validation.hs b/src/Chainweb/BlockHeader/Validation.hs index df5c047b0f..be21654510 100644 --- a/src/Chainweb/BlockHeader/Validation.hs +++ b/src/Chainweb/BlockHeader/Validation.hs @@ -5,7 +5,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeFamilies #-} -- | -- Module: Chainweb.BlockHeader.Validation diff --git a/src/Chainweb/Cut/Create.hs b/src/Chainweb/Cut/Create.hs index a0a6410060..2dd49bcc4d 100644 --- a/src/Chainweb/Cut/Create.hs +++ b/src/Chainweb/Cut/Create.hs @@ -82,12 +82,10 @@ import Chainweb.BlockHash import Chainweb.BlockHeader import Chainweb.BlockHeader.Validation import Chainweb.BlockHeight -import Chainweb.ChainId import Chainweb.ChainValue import Chainweb.Cut import Chainweb.Cut.CutHashes import Chainweb.Difficulty -import Chainweb.Graph import Chainweb.Payload import Chainweb.Time import Chainweb.Utils diff --git a/src/Chainweb/CutDB.hs b/src/Chainweb/CutDB.hs index 7beb82ca7a..33e7f743c0 100644 --- a/src/Chainweb/CutDB.hs +++ b/src/Chainweb/CutDB.hs @@ -821,7 +821,7 @@ member db cid h = do -- -------------------------------------------------------------------------- -- -- Some CutDB --- | 'CutDb' with type level 'ChainwebVersionTag +-- | 'CutDb' with type level 'ChainwebVersionName' -- newtype CutDbT tbl (v :: ChainwebVersionT) = CutDbT (CutDb tbl) deriving (Generic) diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index 15d7a8889e..0062843920 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -75,7 +75,6 @@ import Data.Hashable import qualified Data.Text as T import GHC.Generics -import GHC.Stack import GHC.TypeNats import Text.Printf (printf) @@ -278,7 +277,7 @@ decodeHashDifficultyBe = HashDifficulty <$!> decodePowHashNatBe -- | Given the same `ChainwebVersion`, forms an isomorphism with -- `difficultyToTarget`. -- -targetToDifficulty :: HasCallStack => HashTarget -> HashDifficulty +targetToDifficulty :: HashTarget -> HashDifficulty targetToDifficulty (HashTarget (PowHashNat target)) = HashDifficulty . PowHashNat $ maxTargetWord `div` target {-# INLINE targetToDifficulty #-} @@ -329,8 +328,7 @@ adjust (BlockRate br) (WindowWidth ww) (TimeSpan delta) (HashTarget oldTarget) = -- This is used when 'oldDaGuard' is active. -- legacyAdjust - :: HasCallStack - => BlockRate + :: BlockRate -> WindowWidth -> TimeSpan Micros -- ^ the actual time of the last epoch: creation time minus the epoch diff --git a/src/Chainweb/Mempool/Consensus.hs b/src/Chainweb/Mempool/Consensus.hs index b27fce61ed..57fbb7dfca 100644 --- a/src/Chainweb/Mempool/Consensus.hs +++ b/src/Chainweb/Mempool/Consensus.hs @@ -42,7 +42,6 @@ import System.LogLevel ------------------------------------------------------------------------------ import Chainweb.BlockHeader import Chainweb.BlockHeaderDB -import Chainweb.ChainId import Chainweb.Mempool.InMem import Chainweb.Mempool.Mempool import Chainweb.Payload @@ -52,7 +51,7 @@ import Chainweb.Transaction import Chainweb.TreeDB import Chainweb.Utils import Chainweb.Version -import Chainweb.Version.Guards +import Chainweb.Version.Guards import Chainweb.Storage.Table import Data.LogMessage (JsonLog(..), LogFunction) diff --git a/src/Chainweb/Miner/RestAPI/Server.hs b/src/Chainweb/Miner/RestAPI/Server.hs index a47ecff969..7040b98939 100644 --- a/src/Chainweb/Miner/RestAPI/Server.hs +++ b/src/Chainweb/Miner/RestAPI/Server.hs @@ -51,7 +51,6 @@ import System.Random -- internal modules -import Chainweb.ChainId import Chainweb.Cut (Cut) import Chainweb.Cut.Create import Chainweb.CutDB (awaitNewCutByChainIdStm, _cut) diff --git a/src/Chainweb/Pact/SPV.hs b/src/Chainweb/Pact/SPV.hs index a87ff02538..3f716ba3b8 100644 --- a/src/Chainweb/Pact/SPV.hs +++ b/src/Chainweb/Pact/SPV.hs @@ -57,7 +57,6 @@ import Chainweb.BlockHash as CW import Chainweb.BlockHeader import Chainweb.BlockHeaderDB import Chainweb.BlockHeight -import qualified Chainweb.ChainId as CW import Chainweb.Pact.Service.Types import Chainweb.Pact.Utils (aeson) import Chainweb.Payload diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index f04343fc7f..c397e84463 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -597,44 +597,6 @@ applyUpgrades v cid height logError $ "Upgrade transaction failed! " <> sshow e throwM e --- applyTwentyChainUpgrade --- :: ChainwebVersion --- -> Chainweb.ChainId --- -> BlockHeight --- -> TransactionM p () --- applyTwentyChainUpgrade v cid bh --- | False = do -- atFork To20Chains v cid bh = do --- let txlist = undefined -- v ^?! versionUpgradeTransactions . to20ChainTransactions . onChain cid - --- infoLog $ "Applying 20-chain upgrades on chain " <> sshow cid - --- let txs = undefined -- fmap payloadObj <$> txlist - --- -- --- -- Note (emily): This function does not need to care about --- -- module caching, because it is already seeded with the correct cache --- -- state, and is not updating the module cache, unlike 'applyUpgrades'. --- -- - --- -- traverse_ applyTx txs --- undefined --- | otherwise = return () --- where --- applyTx tx = do --- infoLog $ "Running 20-chain upgrade tx " <> sshow (_cmdHash tx) - --- let i = initStateInterpreter --- $ initCapabilities [mkMagicCapSlot "REMEDIATE"] - --- r <- tryAllSynchronous (runGenesis tx permissiveNamespacePolicy i) --- case r of --- Left e -> do --- logError $ "Upgrade transaction failed: " <> sshow e --- void $! throwM e --- Right _ -> return () - - - jsonErrorResult :: PactError -> Text From a78353f0ca6625683b98043539bfcc036e756e6e Mon Sep 17 00:00:00 2001 From: Lars Kuhtz Date: Wed, 8 Mar 2023 16:01:54 -0800 Subject: [PATCH 34/91] changes from code review --- src/Chainweb/Utils/Rule.hs | 15 +++- src/Chainweb/Version.hs | 157 ++++++++++++++++++++----------------- 2 files changed, 94 insertions(+), 78 deletions(-) diff --git a/src/Chainweb/Utils/Rule.hs b/src/Chainweb/Utils/Rule.hs index d97110f522..a09e5cffec 100644 --- a/src/Chainweb/Utils/Rule.hs +++ b/src/Chainweb/Utils/Rule.hs @@ -1,7 +1,5 @@ {-# language DeriveAnyClass #-} {-# language DeriveGeneric #-} -{-# language DeriveFoldable #-} -{-# language DeriveFunctor #-} {-# language DeriveTraversable #-} {-# language DerivingStrategies #-} {-# language TupleSections #-} @@ -23,6 +21,11 @@ import GHC.Generics -- | `a` values graded by `h`, starting with the highest `h` value and lowering -- as you go deeper, bottoming out with no `h` value at all. Used to efficiently -- represent behaviors that change as the block height increases. +-- +-- Is is optimized for lookups of items at the top of stack. On the blockchain +-- we often lookup chain properties (e.g. forks) where we are interested in the +-- latest occurance. +-- data Rule h a = Above (h, a) (Rule h a) | End a deriving stock (Eq, Ord, Show, Foldable, Functor, Generic, Generic1, Traversable) deriving anyclass (Hashable, NFData) @@ -63,16 +66,18 @@ ruleDropWhile _ t = t -- | A measurement on a rule tells you where a condition starts to be true; at -- the Top, at the Bottom, or Between lower and upper. +-- data Measurement h a = Bottom a | Top (h, a) | Between (h, a) (h, a) -- | Takes a measurement on a rule using a monotone function. +-- measureRule' :: (h -> Bool) -> Rule h a -> Measurement h a measureRule' p ((topH, topA) `Above` topTail) | p topH = Top (topH, topA) | otherwise = go topH topA topTail where go lh la (Above (h, a) t) - | p h = Between (lh, la) (h, a) + | p h = Between (h, a) (lh, la) | otherwise = go h a t go _ _ (End a) = Bottom a measureRule' _ (End a) = Bottom a @@ -82,11 +87,13 @@ measureRule h = measureRule' (\hc -> h >= hc) -- | Returns the elements of the Rule. +-- ruleElems :: h -> Rule h a -> NE.NonEmpty (h, a) ruleElems h (End a) = (h, a) NE.:| [] ruleElems he (Above (h, a) t) = (h, a) `NE.cons` ruleElems he t -- | Checks that a Rule is decreasing, and thus valid. +-- ruleValid :: Ord h => Rule h a -> Bool ruleValid (Above (h, _) t@(Above (h', _) _)) = h > h' && ruleValid t -ruleValid _ = True \ No newline at end of file +ruleValid _ = True diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 1e0d2b4447..810e7d00d2 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -155,8 +155,10 @@ import Data.Singletons import P2P.Peer --- | Data type representing changes to block validation, whether in the payload or in the header. --- Always add new forks at the end, not in the middle of the constructors. +-- | Data type representing changes to block validation, whether in the payload +-- or in the header. Always add new forks at the end, not in the middle of the +-- constructors. +-- data Fork = SlowEpoch | Vuln797Fix @@ -186,54 +188,54 @@ data Fork deriving anyclass (NFData, Hashable) instance HasTextRepresentation Fork where - toText SlowEpoch = "slowEpoch" - toText Vuln797Fix = "vuln797Fix" - toText CoinV2 = "coinV2" - toText PactBackCompat_v16 = "pactBackCompat_v16" - toText ModuleNameFix = "moduleNameFix" - toText SkipTxTimingValidation = "skipTxTimingValidation" - toText OldTargetGuard = "oldTargetGuard" - toText SkipFeatureFlagValidation = "skipFeatureFlagValidation" - toText ModuleNameFix2 = "moduleNameFix2" - toText OldDAGuard = "oldDaGuard" - toText PactEvents = "pactEvents" - toText SPVBridge = "spvBridge" - toText Pact4Coin3 = "pact4Coin3" - toText EnforceKeysetFormats = "enforceKeysetFormats" - toText Pact420 = "pact420" - toText CheckTxHash = "checkTxHash" - toText Chainweb213Pact = "chainweb213Pact" - toText Chainweb214Pact = "chainweb214Pact" - toText Chainweb215Pact = "chainweb215Pact" - toText Pact44NewTrans = "pact44NewTrans" - toText Chainweb216Pact = "chainweb216Pact" - toText Chainweb217Pact = "chainweb217Pact" - toText Chainweb218Pact = "chainweb218Pact" - - fromText "slowEpoch" = return SlowEpoch - fromText "vuln797Fix" = return Vuln797Fix - fromText "coinV2" = return CoinV2 - fromText "pactBackCompat_v16" = return PactBackCompat_v16 - fromText "moduleNameFix" = return ModuleNameFix - fromText "skipTxTimingValidation" = return SkipTxTimingValidation - fromText "oldTargetGuard" = return OldTargetGuard - fromText "skipFeatureFlagValidation" = return SkipFeatureFlagValidation - fromText "moduleNameFix2" = return ModuleNameFix2 - fromText "oldDaGuard" = return OldDAGuard - fromText "pactEvents" = return PactEvents - fromText "spvBridge" = return SPVBridge - fromText "pact4Coin3" = return Pact4Coin3 - fromText "enforceKeysetFormats" = return EnforceKeysetFormats - fromText "pact420" = return Pact420 - fromText "checkTxHash" = return CheckTxHash - fromText "chainweb213Pact" = return Chainweb213Pact - fromText "chainweb214Pact" = return Chainweb214Pact - fromText "chainweb215Pact" = return Chainweb215Pact - fromText "pact44NewTrans" = return Pact44NewTrans - fromText "chainweb216Pact" = return Chainweb216Pact - fromText "chainweb217Pact" = return Chainweb217Pact - fromText "chainweb218Pact" = return Chainweb218Pact - fromText t = throwM . TextFormatException $ "Unknown Chainweb fork: " <> t + toText SlowEpoch = "slowEpoch" + toText Vuln797Fix = "vuln797Fix" + toText CoinV2 = "coinV2" + toText PactBackCompat_v16 = "pactBackCompat_v16" + toText ModuleNameFix = "moduleNameFix" + toText SkipTxTimingValidation = "skipTxTimingValidation" + toText OldTargetGuard = "oldTargetGuard" + toText SkipFeatureFlagValidation = "skipFeatureFlagValidation" + toText ModuleNameFix2 = "moduleNameFix2" + toText OldDAGuard = "oldDaGuard" + toText PactEvents = "pactEvents" + toText SPVBridge = "spvBridge" + toText Pact4Coin3 = "pact4Coin3" + toText EnforceKeysetFormats = "enforceKeysetFormats" + toText Pact420 = "pact420" + toText CheckTxHash = "checkTxHash" + toText Chainweb213Pact = "chainweb213Pact" + toText Chainweb214Pact = "chainweb214Pact" + toText Chainweb215Pact = "chainweb215Pact" + toText Pact44NewTrans = "pact44NewTrans" + toText Chainweb216Pact = "chainweb216Pact" + toText Chainweb217Pact = "chainweb217Pact" + toText Chainweb218Pact = "chainweb218Pact" + + fromText "slowEpoch" = return SlowEpoch + fromText "vuln797Fix" = return Vuln797Fix + fromText "coinV2" = return CoinV2 + fromText "pactBackCompat_v16" = return PactBackCompat_v16 + fromText "moduleNameFix" = return ModuleNameFix + fromText "skipTxTimingValidation" = return SkipTxTimingValidation + fromText "oldTargetGuard" = return OldTargetGuard + fromText "skipFeatureFlagValidation" = return SkipFeatureFlagValidation + fromText "moduleNameFix2" = return ModuleNameFix2 + fromText "oldDaGuard" = return OldDAGuard + fromText "pactEvents" = return PactEvents + fromText "spvBridge" = return SPVBridge + fromText "pact4Coin3" = return Pact4Coin3 + fromText "enforceKeysetFormats" = return EnforceKeysetFormats + fromText "pact420" = return Pact420 + fromText "checkTxHash" = return CheckTxHash + fromText "chainweb213Pact" = return Chainweb213Pact + fromText "chainweb214Pact" = return Chainweb214Pact + fromText "chainweb215Pact" = return Chainweb215Pact + fromText "pact44NewTrans" = return Pact44NewTrans + fromText "chainweb216Pact" = return Chainweb216Pact + fromText "chainweb217Pact" = return Chainweb217Pact + fromText "chainweb218Pact" = return Chainweb218Pact + fromText t = throwM . TextFormatException $ "Unknown Chainweb fork: " <> t instance ToJSON Fork where toJSON = toJSON . toText @@ -271,13 +273,14 @@ instance MerkleHashAlgorithm a => IsMerkleLogEntry a ChainwebHashTag ChainwebVer -- The type of upgrades, which are sets of transactions to run at certain block -- heights during coinbase. +-- data Upgrade = Upgrade { _upgradeTransactions :: [ChainwebTransaction] , _legacyUpgradeIsPrecocious :: Bool - -- ^ when set to `True`, the upgrade transactions are executed using the - -- forks of the next block, rather than the block the upgrade transactions - -- are included in. do not use this for new upgrades unless you are sure - -- you need it, this mostly exists for old upgrades. + -- ^ when set to `True`, the upgrade transactions are executed using the + -- forks of the next block, rather than the block the upgrade + -- transactions are included in. do not use this for new upgrades + -- unless you are sure you need it, this mostly exists for old upgrades. } deriving stock (Generic, Eq) deriving anyclass (NFData) @@ -289,40 +292,46 @@ upgrade txs = Upgrade txs False -- all nodes on the same network. For examples see `Chainweb.Version.Mainnet`, -- `Chainweb.Version.Testnet`, `Chainweb.Version.Development`, and -- `Chainweb.Test.TestVersions`. +-- +-- NOTE: non of the fields should be strict! +-- FIXME: provide a reason +-- data ChainwebVersion = ChainwebVersion { _versionCode :: ChainwebVersionCode - -- ^ The numeric code identifying the Version, must be unique. See `Chainweb.Version.Registry`. + -- ^ The numeric code identifying the Version, must be unique. See + -- `Chainweb.Version.Registry`. , _versionName :: ChainwebVersionName - -- ^ The textual name of the Version, used in almost all REST endpoints. + -- ^ The textual name of the Version, used in almost all REST endpoints. , _versionGraphs :: Rule BlockHeight ChainGraph - -- ^ The chain graphs in the history and at which block heights they apply. + -- ^ The chain graphs in the history and at which block heights they apply. , _versionForks :: HashMap Fork (ChainMap BlockHeight) - -- ^ The block heights on each chain to apply behavioral changes. - -- Interpretation of these is up to the functions in - -- `Chainweb.Version.Guards`. + -- ^ The block heights on each chain to apply behavioral changes. + -- Interpretation of these is up to the functions in + -- `Chainweb.Version.Guards`. , _versionUpgrades :: ChainMap (HashMap BlockHeight Upgrade) - -- ^ The upgrade transactions to execute on each chain at certain block heights. + -- ^ The upgrade transactions to execute on each chain at certain block + -- heights. , _versionBlockRate :: BlockRate - -- ^ The Proof-of-Work `BlockRate` for each `ChainwebVersion`. This is the - -- number of microseconds we expect to pass while a miner mines on various chains, - -- eventually succeeding on one. + -- ^ The Proof-of-Work `BlockRate` for each `ChainwebVersion`. This is + -- the number of microseconds we expect to pass while a miner mines on + -- various chains, eventually succeeding on one. , _versionWindow :: WindowWidth - -- ^ The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. + -- ^ The Proof-of-Work `WindowWidth` for each `ChainwebVersion`. , _versionHeaderBaseSizeBytes :: Natural - -- ^ The size in bytes of the constant portion of the serialized header. This is - -- the header /without/ the adjacent hashes. - -- - -- NOTE: This is internal. For the actual size of the serialized header - -- use 'headerSizeBytes'. + -- ^ The size in bytes of the constant portion of the serialized header. + -- This is the header /without/ the adjacent hashes. + -- + -- NOTE: This is internal. For the actual size of the serialized header + -- use 'headerSizeBytes'. , _versionMaxBlockGasLimit :: Rule BlockHeight (Maybe Natural) - -- ^ The maximum gas limit for an entire block. + -- ^ The maximum gas limit for an entire block. , _versionBootstraps :: [PeerInfo] - -- ^ The locations of the bootstrap peers. + -- ^ The locations of the bootstrap peers. , _versionGenesis :: ChainwebGenesis - -- ^ The information used to construct the genesis blocks. + -- ^ The information used to construct the genesis blocks. , _versionCheats :: Cheats - -- ^ Whether to disable any core functionality. + -- ^ Whether to disable any core functionality. } deriving stock (Generic) deriving anyclass NFData From 8951c527e579c6266693870f6d15526babf1d990 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Mar 2023 09:57:20 -0500 Subject: [PATCH 35/91] split VersionCheats to add VersionDefaults --- src/Chainweb/Chainweb.hs | 2 +- src/Chainweb/Chainweb/Configuration.hs | 4 +-- src/Chainweb/Version.hs | 41 ++++++++++++++++---------- src/Chainweb/Version/Development.hs | 10 ++++--- src/Chainweb/Version/Mainnet.hs | 12 ++++---- src/Chainweb/Version/Testnet.hs | 14 +++++---- src/P2P/Node.hs | 2 +- test/Chainweb/Test/TestVersions.hs | 24 +++++++++------ 8 files changed, 65 insertions(+), 44 deletions(-) diff --git a/src/Chainweb/Chainweb.hs b/src/Chainweb/Chainweb.hs index f20492b19f..16beec3e15 100644 --- a/src/Chainweb/Chainweb.hs +++ b/src/Chainweb/Chainweb.hs @@ -826,7 +826,7 @@ runChainweb cw = do mempoolSyncClients = case enabledConfig mempoolP2pConfig of Nothing -> disabled Just c - | cw ^. chainwebVersion . versionCheats . disableMempoolSync -> disabled + | cw ^. chainwebVersion . versionDefaults . disableMempoolSync -> disabled | otherwise -> enabled c where disabled = do diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 2c345cb88b..b1a317c949 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -416,7 +416,7 @@ validateChainwebConfiguration :: ConfigValidation ChainwebConfiguration [] validateChainwebConfiguration c = do validateMinerConfig (_configMining c) validateBackupConfig (_configBackup c) - unless (c ^. chainwebVersion . versionCheats . disablePeerValidation) $ + unless (c ^. chainwebVersion . versionDefaults . disablePeerValidation) $ validateP2pConfiguration (_configP2p c) validateBackupConfig :: ConfigValidation BackupConfig [] @@ -586,7 +586,7 @@ pChainwebConfiguration = id in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) (HS.toMap (chainIds winningVersion)) ) fub - , _versionCheats = + , _versionCheats = _versionCheats winningVersion & disablePow .~ disablePow' } | Nothing <- br, Nothing <- fub = winningVersion diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 810e7d00d2..1ffc4cd4b6 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -30,8 +30,9 @@ module Chainweb.Version ( -- * Properties of Chainweb Version Fork(..) - , ChainwebGenesis(..) - , Cheats(..) + , VersionGenesis(..) + , VersionCheats(..) + , VersionDefaults(..) , disablePow , fakeFirstEpochStart , disablePact @@ -47,6 +48,7 @@ module Chainweb.Version , versionForks , versionBlockRate , versionCheats + , versionDefaults , versionUpgrades , versionBootstraps , versionCode @@ -328,10 +330,11 @@ data ChainwebVersion -- ^ The maximum gas limit for an entire block. , _versionBootstraps :: [PeerInfo] -- ^ The locations of the bootstrap peers. - , _versionGenesis :: ChainwebGenesis + , _versionGenesis :: VersionGenesis -- ^ The information used to construct the genesis blocks. - , _versionCheats :: Cheats + , _versionCheats :: VersionCheats -- ^ Whether to disable any core functionality. + , _versionDefaults :: VersionDefaults } deriving stock (Generic) deriving anyclass NFData @@ -364,22 +367,27 @@ instance Eq ChainwebVersion where , _versionGenesis v == _versionGenesis v' ] -data Cheats = Cheats +data VersionDefaults = VersionDefaults + { _disablePeerValidation :: Bool + -- ^ should we try to check that a peer is valid? See `P2P.Peer.validatePeerConfig` + , _disableMempoolSync :: Bool + -- ^ should we disable mempool sync entirely? + } + deriving stock (Generic, Eq, Ord, Show) + deriving anyclass (ToJSON, FromJSON, NFData) + +data VersionCheats = VersionCheats { _disablePow :: Bool - -- ^ should we stop checking proof of work? + -- ^ should we stop checking proof of work? , _fakeFirstEpochStart :: Bool - -- ^ should we fake the start time of the first epoch? See `Chainweb.BlockHeader.epochStart`. + -- ^ should we fake the start time of the first epoch? See `Chainweb.BlockHeader.epochStart`. , _disablePact :: Bool - -- ^ Should we replace the pact service with a dummy that always makes empty blocks? - , _disablePeerValidation :: Bool - -- ^ should we try to check that a peer is valid? See `P2P.Peer.validatePeerConfig` - , _disableMempoolSync :: Bool - -- ^ should we disable mempool sync entirely? + -- ^ Should we replace the pact service with a dummy that always makes empty blocks? } deriving stock (Generic, Eq, Ord, Show) deriving anyclass (ToJSON, FromJSON, NFData) -data ChainwebGenesis = ChainwebGenesis +data VersionGenesis = VersionGenesis { _genesisBlockTarget :: ChainMap HashTarget , _genesisBlockPayload :: ChainMap PayloadWithOutputs , _genesisTime :: ChainMap BlockCreationTime @@ -387,12 +395,13 @@ data ChainwebGenesis = ChainwebGenesis deriving stock (Generic, Eq) deriving anyclass NFData -instance Show ChainwebGenesis where +instance Show VersionGenesis where show _ = "" makeLensesWith (lensRules & generateLazyPatterns .~ True) 'ChainwebVersion -makeLensesWith (lensRules & generateLazyPatterns .~ True) 'ChainwebGenesis -makeLensesWith (lensRules & generateLazyPatterns .~ True) 'Cheats +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'VersionGenesis +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'VersionCheats +makeLensesWith (lensRules & generateLazyPatterns .~ True) 'VersionDefaults genesisBlockPayloadHash :: ChainwebVersion -> ChainId -> BlockPayloadHash genesisBlockPayloadHash v cid = v ^?! versionGenesis . genesisBlockPayload . onChain cid . to _payloadWithOutputsPayloadHash diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 024522475f..0e0a56b75d 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -89,7 +89,7 @@ devnet = ChainwebVersion , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionBootstraps = [] - , _versionGenesis = ChainwebGenesis + , _versionGenesis = VersionGenesis { _genesisBlockTarget = onChains $ concat [ [(unsafeChainId i, HashTarget $ maxBound `div` 100_000) | i <- [0..9]] , [(unsafeChainId i, HashTarget 0x0000088f99632cadf39b0db7655be62cb7dbc84ebbd9a90e5b5756d3e7d9196c) | i <- [10..19]] @@ -103,11 +103,13 @@ devnet = ChainwebVersion } , _versionMaxBlockGasLimit = End (Just 180_000) - , _versionCheats = Cheats - { _disablePeerValidation = True - , _disablePow = False + , _versionCheats = VersionCheats + { _disablePow = False , _fakeFirstEpochStart = True , _disablePact = False + } + , _versionDefaults = VersionDefaults + { _disablePeerValidation = True , _disableMempoolSync = False } } diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 08d135b6d9..f1ae0295bf 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -149,7 +149,7 @@ mainnet = ChainwebVersion (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` End Nothing , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts - , _versionGenesis = ChainwebGenesis + , _versionGenesis = VersionGenesis { _genesisBlockTarget = OnChains $ HM.fromList $ concat [ [(unsafeChainId i, maxTarget) | i <- [0..9]] , [(unsafeChainId i, mainnet20InitialHashTarget) | i <- [10..19]] @@ -189,11 +189,13 @@ mainnet = ChainwebVersion , (Chainweb215Pact, AllChains $ Upgrade CoinV5.transactions True) ]) (onChains [(unsafeChainId 0, HM.singleton to20ChainsMainnet (upgrade MNKAD.transactions))]) - , _versionCheats = Cheats - { _disablePeerValidation = False - , _disablePow = False + , _versionCheats = VersionCheats + { _disablePow = False , _fakeFirstEpochStart = False - , _disableMempoolSync = False , _disablePact = False } + , _versionDefaults = VersionDefaults + { _disablePeerValidation = False + , _disableMempoolSync = False + } } diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index ea8df87c4e..30ceafd083 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -129,7 +129,7 @@ testnet = ChainwebVersion (succ $ testnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` End Nothing , _versionBootstraps = domainAddr2PeerInfo testnetBootstrapHosts - , _versionGenesis = ChainwebGenesis + , _versionGenesis = VersionGenesis { _genesisBlockTarget = OnChains $ HM.fromList $ concat [ [(unsafeChainId i, maxTarget) | i <- [0..9]] , [(unsafeChainId i, testnet20InitialHashTarget) | i <- [10..19]] @@ -160,11 +160,13 @@ testnet = ChainwebVersion , (Chainweb215Pact, AllChains (Upgrade CoinV5.transactions True)) ]) (onChains [(unsafeChainId 0, HM.singleton to20ChainsTestnet (upgrade MNKAD.transactions))]) - , _versionCheats = Cheats - { _disablePeerValidation = False - , _disablePow = False + , _versionCheats = VersionCheats + { _disablePow = False , _fakeFirstEpochStart = False - , _disableMempoolSync = False , _disablePact = False } - } + , _versionDefaults = VersionDefaults + { _disablePeerValidation = False + , _disableMempoolSync = False + } + } \ No newline at end of file diff --git a/src/P2P/Node.hs b/src/P2P/Node.hs index c197e52ec5..86f520feca 100644 --- a/src/P2P/Node.hs +++ b/src/P2P/Node.hs @@ -384,7 +384,7 @@ guardPeerDb v nid peerDb pinf = do else return $ Left $ NodeVersionNotAccepted pinf nodeVersion where isReserved :: Bool - isReserved = not (v ^. versionCheats . disablePeerValidation) && isReservedHostAddress (_peerAddr pinf) + isReserved = not (v ^. versionDefaults . disablePeerValidation) && isReservedHostAddress (_peerAddr pinf) -- Currently we are using 'getNewPeerManager' which doesn't validate -- certificates. We could be more strict and check that the certificate diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 9841661139..40884d9699 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -141,14 +141,16 @@ barebonesTestVersion' g v = & versionBlockRate .~ BlockRate 1_000_000 & versionName .~ ChainwebVersionName ("test-" <> toText g) & versionGraphs .~ End g - & versionCheats .~ Cheats + & versionCheats .~ VersionCheats { _disablePow = True , _fakeFirstEpochStart = True , _disablePact = True - , _disableMempoolSync = True + } + & versionDefaults .~ VersionDefaults + { _disableMempoolSync = True , _disablePeerValidation = True } - & versionGenesis .~ ChainwebGenesis + & versionGenesis .~ VersionGenesis { _genesisBlockPayload = AllChains emptyPayload , _genesisBlockTarget = AllChains maxTarget , _genesisTime = AllChains $ BlockCreationTime epoch @@ -166,14 +168,16 @@ cpmTestVersion g v = v & versionWindow .~ WindowWidth 120 & versionBlockRate .~ BlockRate (Micros 100_000) & versionGraphs .~ End g - & versionCheats .~ Cheats + & versionCheats .~ VersionCheats { _disablePow = True , _fakeFirstEpochStart = True , _disablePact = False - , _disableMempoolSync = False + } + & versionDefaults .~ VersionDefaults + { _disableMempoolSync = False , _disablePeerValidation = True } - & versionGenesis .~ ChainwebGenesis + & versionGenesis .~ VersionGenesis { _genesisBlockPayload = onChains $ (unsafeChainId 0, TN0.payloadBlock) : [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] @@ -257,14 +261,16 @@ timedConsensusVersion' g1 g2 v = & versionUpgrades .~ AllChains HM.empty & versionWindow .~ WindowWidth 120 & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) - & versionCheats .~ Cheats + & versionCheats .~ VersionCheats { _disablePow = True , _fakeFirstEpochStart = True , _disablePact = True - , _disableMempoolSync = True + } + & versionDefaults .~ VersionDefaults + { _disableMempoolSync = True , _disablePeerValidation = True } - & versionGenesis .~ ChainwebGenesis + & versionGenesis .~ VersionGenesis { _genesisBlockPayload = onChains $ (unsafeChainId 0, TN0.payloadBlock) : [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] From c09db18ccbaa953dd9a8ca49c97dc2299049812d Mon Sep 17 00:00:00 2001 From: Lars Kuhtz Date: Fri, 10 Mar 2023 15:11:31 -0800 Subject: [PATCH 36/91] remove redundant pragma --- src/Chainweb/Version/Registry.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index 1734bf5183..9f8d644e3e 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -1,5 +1,3 @@ -{-# language RecordWildCards #-} - -- | -- Module: Chainweb.Version.Registry -- Copyright: Copyright © 2023 Kadena LLC. From 23dd634e520b6d7f6a0eb9f4c72ee769d343a089 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 13 Mar 2023 12:10:45 -0400 Subject: [PATCH 37/91] Specific memoization for mainnet and testnet genesis headers --- src/Chainweb/BlockHeader.hs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 67337d4f0d..3c613d7916 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -164,6 +164,7 @@ import Chainweb.Utils.Serialization import Chainweb.Version import Chainweb.Version.Guards import Chainweb.Version.Mainnet +import Chainweb.Version.Testnet import Chainweb.Version.Registry import Chainweb.Storage.Table @@ -637,11 +638,7 @@ genesisParentBlockHash v p = BlockHash $ MerkleLogHash {-# NOINLINE genesisBlockHeaderCache #-} genesisBlockHeaderCache :: IORef (HashMap ChainwebVersionCode (HashMap ChainId BlockHeader)) genesisBlockHeaderCache = unsafePerformIO $ do - let mkMainnetHeader = makeGenesisBlockHeader mainnet - newIORef $ HM.singleton (_versionCode mainnet) $ HM.fromList - [ (cid, mkMainnetHeader cid) - | cid <- HS.toList (chainIds mainnet) - ] + newIORef HM.empty -- | A block chain is globally uniquely identified by its genesis hash. -- Internally, we use the 'ChainwebVersionTag value and the 'ChainId' @@ -652,19 +649,26 @@ genesisBlockHeaderCache = unsafePerformIO $ do -- scope and identify chains only by their internal 'ChainId'. -- genesisBlockHeaders :: ChainwebVersion -> HashMap ChainId BlockHeader -genesisBlockHeaders v = unsafePerformIO $ - HM.lookup (_versionCode v) <$> readIORef genesisBlockHeaderCache >>= \case - Just hs -> return hs - Nothing -> do - modifyIORef' genesisBlockHeaderCache $ HM.insert (_versionCode v) freshGenesisHeaders - return freshGenesisHeaders +genesisBlockHeaders = \v -> + if _versionCode v == _versionCode mainnet then mainnetGenesisHeaders + else if _versionCode v == _versionCode testnet then testnetGenesisHeaders + else unsafeDupablePerformIO $ + HM.lookup (_versionCode v) <$> readIORef genesisBlockHeaderCache >>= \case + Just hs -> return hs + Nothing -> do + let freshGenesisHeaders = makeGenesisBlockHeaders v + modifyIORef' genesisBlockHeaderCache $ HM.insert (_versionCode v) freshGenesisHeaders + return freshGenesisHeaders where - freshGenesisHeaders = - HM.fromList [ (cid, makeGenesisBlockHeader v cid) | cid <- HS.toList (chainIds v) ] + mainnetGenesisHeaders = makeGenesisBlockHeaders mainnet + testnetGenesisHeaders = makeGenesisBlockHeaders testnet genesisBlockHeader :: (HasCallStack, HasChainId p) => ChainwebVersion -> p -> BlockHeader genesisBlockHeader v p = genesisBlockHeaders v ^?! at (_chainId p) . _Just +makeGenesisBlockHeaders :: ChainwebVersion -> HashMap ChainId BlockHeader +makeGenesisBlockHeaders v = HM.fromList [ (cid, makeGenesisBlockHeader v cid) | cid <- HS.toList (chainIds v)] + makeGenesisBlockHeader :: ChainwebVersion -> ChainId -> BlockHeader makeGenesisBlockHeader v cid = makeGenesisBlockHeader' v cid (_genesisTime (_versionGenesis v) ^?! onChain cid) (Nonce 0) From db579b5e5071f8c83073c68d307a8de0c3310f64 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 13 Mar 2023 12:47:36 -0400 Subject: [PATCH 38/91] instance HasChainwebVersion ChainwebVersionCode --- src/Chainweb/BlockHeader.hs | 27 ++++++++++--------- src/Chainweb/BlockHeader/Validation.hs | 4 +-- .../Pact/Backend/RelationalCheckpointer.hs | 2 +- src/Chainweb/Pact/Types.hs | 2 +- src/Chainweb/Rosetta/Internal.hs | 2 +- src/Chainweb/Rosetta/Utils.hs | 2 +- src/Chainweb/TreeDB/RemoteDB.hs | 2 +- src/Chainweb/Version/Registry.hs | 4 +++ test/Chainweb/Test/BlockHeader/Validation.hs | 12 ++++----- .../Chainweb/Test/BlockHeaderDB/PruneForks.hs | 2 +- test/Chainweb/Test/Mempool/Consensus.hs | 2 +- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 14 +++++----- test/Chainweb/Test/Pact/TransactionTests.hs | 2 +- test/Chainweb/Test/Utils.hs | 2 +- test/Chainweb/Test/Utils/BlockHeader.hs | 8 +++--- 15 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/Chainweb/BlockHeader.hs b/src/Chainweb/BlockHeader.hs index 3c613d7916..706c4e78b9 100644 --- a/src/Chainweb/BlockHeader.hs +++ b/src/Chainweb/BlockHeader.hs @@ -165,7 +165,7 @@ import Chainweb.Version import Chainweb.Version.Guards import Chainweb.Version.Mainnet import Chainweb.Version.Testnet -import Chainweb.Version.Registry +import Chainweb.Version.Registry () import Chainweb.Storage.Table @@ -340,7 +340,7 @@ data BlockHeader :: Type where -- the block height of a block is the block height of its parent -- plus one. - , _blockChainwebVersion :: !ChainwebVersion + , _blockChainwebVersion :: !ChainwebVersionCode -- ^ the Chainweb version is a constant for the chain. A chain -- is uniquely identified by its genesis block. Thus this is -- redundant information and thus subject to the inductive property @@ -377,10 +377,10 @@ instance HasChainId BlockHeader where _chainId = _blockChainId instance HasChainGraph BlockHeader where - _chainGraph h = _chainGraph (_blockChainwebVersion h, _blockHeight h) + _chainGraph h = _chainGraph (_chainwebVersion h, _blockHeight h) instance HasChainwebVersion BlockHeader where - _chainwebVersion = _blockChainwebVersion + _chainwebVersion = _chainwebVersion . _blockChainwebVersion instance IsCasValue BlockHeader where type CasKeyType BlockHeader = BlockHash @@ -423,8 +423,9 @@ slowEpoch :: ParentHeader -> BlockCreationTime -> Bool slowEpoch (ParentHeader p) (BlockCreationTime ct) = actual > (expected * 5) where EpochStartTime es = _blockEpochStart p - BlockRate br = _versionBlockRate (_blockChainwebVersion p) - WindowWidth ww = _versionWindow (_blockChainwebVersion p) + v = _chainwebVersion p + BlockRate br = _versionBlockRate v + WindowWidth ww = _versionWindow v expected :: Micros expected = br * int ww @@ -786,7 +787,7 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh :+: _blockChainId bh :+: _blockWeight bh :+: _blockHeight bh - :+: _versionCode (_blockChainwebVersion bh) + :+: _blockChainwebVersion bh :+: _blockEpochStart bh :+: _blockNonce bh :+: MerkleLogBody (blockHashRecordToVector $ _blockAdjacentHashes bh) @@ -801,7 +802,7 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh , _blockChainId = cid , _blockWeight = weight , _blockHeight = height - , _blockChainwebVersion = cwv + , _blockChainwebVersion = cwvc , _blockEpochStart = es , _blockNonce = nonce , _blockAdjacentHashes = blockHashRecordFromVector adjGraph cid adjParents @@ -820,7 +821,7 @@ instance HasMerkleLog ChainwebMerkleHashAlgorithm ChainwebHashTag BlockHeader wh :+: nonce :+: MerkleLogBody adjParents ) = _merkleLogEntries l - cwv = lookupVersionByCode cwvc + cwv = _chainwebVersion cwvc adjGraph | height == genesisHeight' cwv cid = chainGraphAt cwv height @@ -837,7 +838,7 @@ encodeBlockHeaderWithoutHash b = do encodeChainId (_blockChainId b) encodeBlockWeight (_blockWeight b) encodeBlockHeight (_blockHeight b) - encodeChainwebVersionCode (_versionCode $ _blockChainwebVersion b) + encodeChainwebVersionCode (_blockChainwebVersion b) encodeEpochStartTime (_blockEpochStart b) encodeNonce (_blockNonce b) @@ -917,7 +918,7 @@ decodeBlockHeader = BlockHeader <*> decodeChainId <*> decodeBlockWeight <*> decodeBlockHeight - <*> (lookupVersionByCode <$> decodeChainwebVersionCode) + <*> decodeChainwebVersionCode <*> decodeEpochStartTime <*> decodeNonce <*> decodeBlockHash @@ -1008,7 +1009,7 @@ blockHeaderProperties (ObjectEncoded b) = , "chainId" .= _chainId b , "weight" .= _blockWeight b , "height" .= _blockHeight b - , "chainwebVersion" .= _versionCode (_blockChainwebVersion b) + , "chainwebVersion" .= _blockChainwebVersion b , "epochStart" .= _blockEpochStart b , "featureFlags" .= _blockFlags b , "hash" .= _blockHash b @@ -1032,7 +1033,7 @@ parseBlockHeaderObject o = BlockHeader <*> o .: "chainId" <*> o .: "weight" <*> o .: "height" - <*> (lookupVersionByCode <$> o .: "chainwebVersion") + <*> o .: "chainwebVersion" <*> o .: "epochStart" <*> o .: "nonce" <*> o .: "hash" diff --git a/src/Chainweb/BlockHeader/Validation.hs b/src/Chainweb/BlockHeader/Validation.hs index be21654510..43469db35c 100644 --- a/src/Chainweb/BlockHeader/Validation.hs +++ b/src/Chainweb/BlockHeader/Validation.hs @@ -688,7 +688,7 @@ prop_block_genesis_parent b && hasGenesisParentHash b ==> isGenesisBlockHeader b where hasGenesisParentHash b' = - _blockParent b' == genesisParentBlockHash (_blockChainwebVersion b') (_chainId b') + _blockParent b' == genesisParentBlockHash (_chainwebVersion b') (_chainId b') prop_block_genesis_target :: BlockHeader -> Bool prop_block_genesis_target b = isGenesisBlockHeader b @@ -798,7 +798,7 @@ prop_block_adjacent_parents (WebStep as (ChainStep _ b)) prop_block_adjacent_parents_version :: WebStep -> Bool prop_block_adjacent_parents_version (WebStep as (ChainStep _ b)) - = all ((== v) . _blockChainwebVersion . _parentHeader) as + = all ((== v) . _chainwebVersion . _parentHeader) as where v = _chainwebVersion b diff --git a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs index efc0244aca..a0f764796a 100644 --- a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs +++ b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs @@ -326,7 +326,7 @@ doGetBlockHistory dbenv blockHeader d = runBlockEnv dbenv $ do !prev <- M.fromList . catMaybes <$> mapM (queryPrev db tname startTxId) (S.toList hkeys) return $ BlockTxHistory tmap prev where - v = _blockChainwebVersion blockHeader + v = _chainwebVersion blockHeader cid = _blockChainId blockHeader bHeight = _blockHeight blockHeader diff --git a/src/Chainweb/Pact/Types.hs b/src/Chainweb/Pact/Types.hs index feaf57d63d..dcf9d1c226 100644 --- a/src/Chainweb/Pact/Types.hs +++ b/src/Chainweb/Pact/Types.hs @@ -535,7 +535,7 @@ ctxChainId :: TxContext -> ChainId ctxChainId = _blockChainId . ctxBlockHeader ctxVersion :: TxContext -> ChainwebVersion -ctxVersion = _blockChainwebVersion . ctxBlockHeader +ctxVersion = _chainwebVersion . ctxBlockHeader -- | Assemble tx context from transaction metadata and parent header. getTxContext :: PublicMeta -> PactServiceM tbl TxContext diff --git a/src/Chainweb/Rosetta/Internal.hs b/src/Chainweb/Rosetta/Internal.hs index 68e901256f..5c40347cf9 100644 --- a/src/Chainweb/Rosetta/Internal.hs +++ b/src/Chainweb/Rosetta/Internal.hs @@ -125,7 +125,7 @@ matchLogs typ bh logs coinbase txs where bheight = _blockHeight bh cid = _blockChainId bh - v = _blockChainwebVersion bh + v = _chainwebVersion bh matchGenesis = hoistEither $ case typ of FullLogs -> genesisTransactions logs cid txs diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index 7a5f1a1b72..82efdef309 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -851,7 +851,7 @@ parentBlockId bh where bHeight = _blockHeight bh cid = _blockChainId bh - v = _blockChainwebVersion bh + v = _chainwebVersion bh parent = BlockId { _blockId_index = getBlockHeight (pred $ _blockHeight bh) , _blockId_hash = blockHashToText (_blockParent bh) diff --git a/src/Chainweb/TreeDB/RemoteDB.hs b/src/Chainweb/TreeDB/RemoteDB.hs index 6890eef953..8e304454c3 100644 --- a/src/Chainweb/TreeDB/RemoteDB.hs +++ b/src/Chainweb/TreeDB/RemoteDB.hs @@ -136,4 +136,4 @@ remoteDb -> IO RemoteDb remoteDb db logg env = do h <- root db - pure $! RemoteDb env (ALogFunction logg) (_blockChainwebVersion h) (_blockChainId h) + pure $! RemoteDb env (ALogFunction logg) (_chainwebVersion h) (_blockChainId h) diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index 9f8d644e3e..f1afbb1fad 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -1,3 +1,4 @@ +{-# OPTIONS_GHC -Wno-orphans #-} -- | -- Module: Chainweb.Version.Registry -- Copyright: Copyright © 2023 Kadena LLC. @@ -118,3 +119,6 @@ findKnownVersion vn = case find (\v -> _versionName v == vn) knownVersions of Nothing -> fail $ T.unpack (getChainwebVersionName vn) <> " is not a known version: try development, mainnet01 or testnet04" Just v -> return v + +instance HasChainwebVersion ChainwebVersionCode where + _chainwebVersion = lookupVersionByCode \ No newline at end of file diff --git a/test/Chainweb/Test/BlockHeader/Validation.hs b/test/Chainweb/Test/BlockHeader/Validation.hs index d3303ed178..d970b31954 100644 --- a/test/Chainweb/Test/BlockHeader/Validation.hs +++ b/test/Chainweb/Test/BlockHeader/Validation.hs @@ -87,7 +87,7 @@ prop_featureFlag :: ChainwebVersion -> BlockHeight -> TestTree prop_featureFlag v h = testCase ("Invalid feature flags fail validation for " <> sshow v) $ do hdr <- (blockHeight .~ h) . (blockFlags .~ fromJuste (decode "1")) - . (blockChainwebVersion .~ v) + . (blockChainwebVersion .~ _versionCode v) <$> generate arbitrary let r = prop_block_featureFlags hdr assertBool @@ -238,7 +238,7 @@ validationFailures = , ( hdr & testHeaderHdr . blockChainId .~ unsafeChainId 1 , [IncorrectHash, IncorrectPow, ChainMismatch, AdjacentChainMismatch] ) - , ( hdr & testHeaderHdr . blockChainwebVersion .~ Development + , ( hdr & testHeaderHdr . blockChainwebVersion .~ _versionCode Development , [IncorrectHash, IncorrectPow, VersionMismatch, InvalidFeatureFlags, CreatedBeforeParent, AdjacentChainMismatch, InvalidAdjacentVersion] ) , ( hdr & testHeaderHdr . blockWeight .~ 10 @@ -273,7 +273,7 @@ validationFailures = , ( hdr & testHeaderHdr . blockAdjacentHashes .~ BlockHashRecord mempty , [IncorrectHash, IncorrectPow, AdjacentChainMismatch] ) - , ( hdr & testHeaderAdjs . each . parentHeader . blockChainwebVersion .~ Development + , ( hdr & testHeaderAdjs . each . parentHeader . blockChainwebVersion .~ _versionCode Development , [InvalidAdjacentVersion] ) , ( hdr & testHeaderAdjs . ix 0 . parentHeader . blockChainId .~ unsafeChainId 0 @@ -355,19 +355,19 @@ daValidation = expected = [IncorrectHash, IncorrectPow, AdjacentChainMismatch] -- From mainnet - hdr = set (h . blockChainwebVersion) Development + hdr = set (h . blockChainwebVersion) (_versionCode Development) $ set (h . blockFlags) mkFeatureFlags $ set (h . blockHeight) 600000 $ set (h . blockEpochStart) (EpochStartTime (hour ^+. epoch)) $ set (h . blockTarget) ((view (p . blockTarget) hdr')) $ set (h . blockCreationTime) (BlockCreationTime (scaleTimeSpan @Int 2 hour ^+. epoch)) - $ set (p . blockChainwebVersion) Development + $ set (p . blockChainwebVersion) (_versionCode Development) $ set (p . blockCreationTime) (BlockCreationTime (hour ^+. epoch)) $ set (p . blockEpochStart) (EpochStartTime epoch) $ set (p . blockHeight) 599999 - $ set (a . blockChainwebVersion) Development + $ set (a . blockChainwebVersion) (_versionCode Development) $ set (a . blockCreationTime) (BlockCreationTime (hour ^+. epoch)) $ set (a . blockTarget) (view (p . blockTarget) hdr') $ set (a . blockEpochStart) (EpochStartTime epoch) diff --git a/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs b/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs index 72df1179b3..9893f824ea 100644 --- a/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs +++ b/test/Chainweb/Test/BlockHeaderDB/PruneForks.hs @@ -275,7 +275,7 @@ failIntrinsicCheck rio checks n step = withDbs rio $ \rdb bdb pdb h -> do (f0, _) <- createForks bdb pdb h let b = f0 !! int n delHdr bdb b - unsafeInsertBlockHeaderDb bdb $ b { _blockChainwebVersion = Development } + unsafeInsertBlockHeaderDb bdb $ b { _blockChainwebVersion = _versionCode Development } try (pruneAllChains logger rdb toyVersion checks) >>= \case Left e | CheckFull `elem` checks diff --git a/test/Chainweb/Test/Mempool/Consensus.hs b/test/Chainweb/Test/Mempool/Consensus.hs index eab69c5e7c..a40ce878a2 100644 --- a/test/Chainweb/Test/Mempool/Consensus.hs +++ b/test/Chainweb/Test/Mempool/Consensus.hs @@ -357,7 +357,7 @@ header' h = do where BlockCreationTime t = _blockCreationTime h target = powTarget (ParentHeader h) mempty t' - v = _blockChainwebVersion h + v = _chainwebVersion h t' = BlockCreationTime (scaleTimeSpan (10 :: Int) second `add` t) ---------------------------------------------------------------------------------------------------- diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index f5716a23ff..493cd8b538 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -245,9 +245,9 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(enforce false 'hi)") $ assertTxFailure "Should fail with the error from the enforce" "hi" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce pre-fork evaluates the string with gas" 35 + assertTxGas "Enforce pre-fork evaluates the string with gas" 34 , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ - assertTxGas "List functions pre-fork gas" 20 + assertTxGas "List functions pre-fork gas" 19 , PactTxTest (buildBasicGas 70000 $ tblModule "Tbl") $ assertTxSuccess "mod53 table update succeeds" $ pString "TableCreated" @@ -259,9 +259,9 @@ pact45UpgradeTest = do [ PactTxTest (buildSimpleCmd "(+ 1 \'clearlyanerror)") $ assertTxFailure "Should replace tx error with empty error" "" , PactTxTest (buildSimpleCmd "(enforce true (format \"{}-{}\" [12345, 657859]))") $ - assertTxGas "Enforce post fork does not eval the string" (15 + coinTxBuyTransferGas) + assertTxGas "Enforce post fork does not eval the string" (14 + coinTxBuyTransferGas) , PactTxTest (buildSimpleCmd "(enumerate 0 10) (str-to-list 'hi) (make-list 10 'hi)") $ - assertTxGas "List functions post-fork change gas" (40 + coinTxBuyTransferGas) + assertTxGas "List functions post-fork change gas" (39 + coinTxBuyTransferGas) , PactTxTest (buildBasicGas 70000 $ tblModule "tBl") $ assertTxFailure "mod53 table update fails after fork" "" @@ -657,7 +657,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Pre-fork format gas" 21 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Pre-fork try" 19 + assertTxGas "Pre-fork try" 18 , PactTxTest (buildSimpleCmd defineNonNamespacedPreFork) $ assertTxSuccess "Should pass when defining a non-namespaced keyset" @@ -673,7 +673,7 @@ chainweb216Test = do [ PactTxTest (buildSimpleCmd formatGas) $ assertTxGas "Post-fork format gas increase" 48 , PactTxTest (buildSimpleCmd tryGas) $ - assertTxGas "Post-fork try should charge a bit more gas" 20 + assertTxGas "Post-fork try should charge a bit more gas" 19 , PactTxTest (buildSimpleCmd defineNonNamespacedPostFork1) $ assertTxFailure "Should fail when defining a non-namespaced keyset post fork" @@ -877,7 +877,7 @@ pact4coin3UpgradeTest = do "coin" v3Hash assertTxEvents "Events for tx1 @ block 22" [gasEv1,allocEv,allocTfr] cr , PactTxTest (buildXSend []) $ \cr -> do - gasEv2 <- mkTransferEvent "sender00" "NoMiner" 0.0015 "coin" v3Hash + gasEv2 <- mkTransferEvent "sender00" "NoMiner" 0.0014 "coin" v3Hash sendTfr <- mkTransferEvent "sender00" "" 0.0123 "coin" v3Hash yieldEv <- mkXYieldEvent "sender00" "sender00" 0.0123 sender00Ks "pact" v3Hash "0" "0" assertTxEvents "Events for tx2 @ block 22" [gasEv2,sendTfr, yieldEv] cr diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 890c2d2eda..ae125a9774 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -384,7 +384,7 @@ matchLogs expectedResults actualResults parent :: BlockHeight -> V.ChainId -> ParentHeader parent bh cid = ParentHeader (someBlockHeader v bh) - { _blockChainwebVersion = v + { _blockChainwebVersion = _versionCode v , _blockChainId = cid , _blockHeight = pred bh } diff --git a/test/Chainweb/Test/Utils.hs b/test/Chainweb/Test/Utils.hs index e5b4d77312..6edb2b8d95 100644 --- a/test/Chainweb/Test/Utils.hs +++ b/test/Chainweb/Test/Utils.hs @@ -485,7 +485,7 @@ header p = do where BlockCreationTime t = _blockCreationTime p target = powTarget (ParentHeader p) mempty t' - v = _blockChainwebVersion p + v = _chainwebVersion p t' = BlockCreationTime (scaleTimeSpan (10 :: Int) second `add` t) -- | get arbitrary value for seed. diff --git a/test/Chainweb/Test/Utils/BlockHeader.hs b/test/Chainweb/Test/Utils/BlockHeader.hs index ebfcc485be..ec93a8d6de 100644 --- a/test/Chainweb/Test/Utils/BlockHeader.hs +++ b/test/Chainweb/Test/Utils/BlockHeader.hs @@ -68,7 +68,7 @@ testPayload n = newPayloadWithOutputs -- testBlockPayloadFromParent :: ParentHeader -> PayloadWithOutputs testBlockPayloadFromParent (ParentHeader b) = testPayload $ B8.intercalate "," - [ sshow (_blockChainwebVersion b) + [ sshow (_chainwebVersion b) , sshow (_blockHeight b + 1) ] @@ -80,7 +80,7 @@ testBlockPayloadFromParent (ParentHeader b) = testPayload $ B8.intercalate "," -- testBlockPayload :: BlockHeader -> PayloadWithOutputs testBlockPayload b = testPayload $ B8.intercalate "," - [ sshow (_blockChainwebVersion b) + [ sshow (_chainwebVersion b) , sshow (_blockHeight b) ] @@ -93,7 +93,7 @@ testBlockPayload b = testPayload $ B8.intercalate "," -- testBlockPayloadFromParent_ :: Nonce -> ParentHeader -> PayloadWithOutputs testBlockPayloadFromParent_ n (ParentHeader b) = testPayload $ B8.intercalate "," - [ sshow (_blockChainwebVersion b) + [ sshow (_chainwebVersion b) , sshow (_blockHeight b + 1) , sshow n ] @@ -106,7 +106,7 @@ testBlockPayloadFromParent_ n (ParentHeader b) = testPayload $ B8.intercalate ", -- testBlockPayload_ :: BlockHeader -> PayloadWithOutputs testBlockPayload_ b = testPayload $ B8.intercalate "," - [ sshow (_blockChainwebVersion b) + [ sshow (_chainwebVersion b) , sshow (_blockHeight b) , sshow (_blockNonce b) ] From 5c73a901ff633d1bdbda7d50ae4f0e68acc9d2dd Mon Sep 17 00:00:00 2001 From: Lars Kuhtz Date: Tue, 14 Mar 2023 17:19:47 -0700 Subject: [PATCH 39/91] small changes from review --- src/Chainweb/Chainweb/Configuration.hs | 2 -- src/Chainweb/Version.hs | 2 +- test/Chainweb/Test/TestVersions.hs | 4 +--- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index b1a317c949..d61bf74dcd 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -18,8 +18,6 @@ -- Maintainer: Lars Kuhtz -- Stability: experimental -- --- TODO --- module Chainweb.Chainweb.Configuration ( -- * Throttling Configuration diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 1ffc4cd4b6..15236b805c 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -454,7 +454,7 @@ instance SingKind ChainwebVersionT where SomeChainwebVersionT p -> SomeSing (singByProxy p) pattern FromSingChainwebVersion :: Sing (n :: ChainwebVersionT) -> ChainwebVersion -pattern FromSingChainwebVersion sng <- ((\v -> withSomeSing (_versionName v) SomeSing) -> SomeSing sng) +pattern FromSingChainwebVersion sng <- (\v -> withSomeSing (_versionName v) SomeSing -> SomeSing sng) {-# COMPLETE FromSingChainwebVersion #-} -------------------------------------------------------------------------- -- diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 40884d9699..0e70293f13 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -2,8 +2,6 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE RecursiveDo #-} {-# LANGUAGE ScopedTypeVariables #-} module Chainweb.Test.TestVersions @@ -242,7 +240,7 @@ noBridgeCpmTestVersion' :: ChainGraph -> VersionBuilder noBridgeCpmTestVersion' g v = cpmTestVersion g (testVersionTemplate v) & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) - & versionForks .~ (fastForks & at SPVBridge .~ Just (AllChains maxBound)) + & versionForks .~ (fastForks & at SPVBridge ?~ AllChains maxBound) timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion timedConsensusVersion g1 g2 = legalizeTestVersion (timedConsensusVersion' g1 g2) From 15ef99d2007147b05f34a94444071fb8d4bfe8d4 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 13:24:53 -0400 Subject: [PATCH 40/91] Change forks and upgrades for barebonesTestVersion --- test/Chainweb/Test/TestVersions.hs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 0e70293f13..89a22159b5 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -153,13 +153,8 @@ barebonesTestVersion' g v = , _genesisBlockTarget = AllChains maxTarget , _genesisTime = AllChains $ BlockCreationTime epoch } - & versionForks .~ fastForks - & versionUpgrades .~ forkUpgrades v - [ (CoinV2, AllChains (upgrade Other.transactions)) - , (Pact4Coin3, AllChains (upgrade CoinV3.transactions)) - , (Chainweb214Pact, AllChains (upgrade CoinV4.transactions)) - , (Chainweb215Pact, AllChains (upgrade CoinV5.transactions)) - ] + & versionForks .~ HM.fromList [ (f, AllChains $ BlockHeight 0) | f <- [minBound..maxBound] ] + & versionUpgrades .~ AllChains HM.empty cpmTestVersion :: ChainGraph -> VersionBuilder cpmTestVersion g v = v From 689572519b9b20f019ff89fd5cc0c42ac2cb82e9 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 14:26:54 -0400 Subject: [PATCH 41/91] More docs and better TestVersions --- src/Chainweb/Version.hs | 9 +- test/Chainweb/Test/TestVersions.hs | 201 ++++++++++++++--------------- 2 files changed, 107 insertions(+), 103 deletions(-) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 15236b805c..0d889feb62 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -295,8 +295,13 @@ upgrade txs = Upgrade txs False -- `Chainweb.Version.Testnet`, `Chainweb.Version.Development`, and -- `Chainweb.Test.TestVersions`. -- --- NOTE: non of the fields should be strict! --- FIXME: provide a reason +-- NOTE: none of the fields should be strict at any level, because of how we +-- use them in `Chainweb.Test.TestVersions`. However, all versions are +-- evaluated to normal form by `Chainweb.Version.Registry` before use. We also +-- explicitly produce lazy optics which are required by +-- `Chainweb.Test.TestVersions`. Only the mutation side of the optics is lazy, +-- and mutating a chainweb version during normal operation of a node is +-- completely illegal. -- data ChainwebVersion = ChainwebVersion diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 89a22159b5..14e783eced 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -5,8 +5,7 @@ {-# LANGUAGE ScopedTypeVariables #-} module Chainweb.Test.TestVersions - ( legalizeTestVersion - , barebonesTestVersion + ( barebonesTestVersion , fastForkingCpmTestVersion , noBridgeCpmTestVersion , slowForkingCpmTestVersion @@ -66,43 +65,51 @@ testBootstrapPeerInfos = type VersionBuilder = ChainwebVersion -> ChainwebVersion -legalizeTestVersion :: VersionBuilder -> ChainwebVersion -legalizeTestVersion f = unsafePerformIO $ do - let v = f v - registerVersion v - return v +-- | Executes a `VersionBuilder` to build a `ChainwebVersion`, by taking its +-- fixed point. Additionally registers it in the global version registry. +buildTestVersion :: VersionBuilder -> ChainwebVersion +buildTestVersion f = + unsafeDupablePerformIO (v <$ registerVersion v) & versionName .~ v ^. versionName + where + v = f v --- | All chainweb versions used in tests *must* be included in this list to be assigned --- a version code, and also registered via legalizeTestVersion into the version registry. --- Failure to do so will result in runtime errors. -testRegistry :: [ChainwebVersionName] -testRegistry = concat - [ [ _versionName $ fastForkingCpmTestVersion' (knownChainGraph g) undefined +-- | All testing `ChainwebVersion`s *must* have unique names and must be +-- included in this list to be assigned a version code, and also registered via +-- `buildTestVersion` into the global version registry. Failure to do so will +-- result in runtime errors from `Chainweb.Version.Registry`. +testVersions :: [ChainwebVersionName] +testVersions = _versionName <$> concat + [ [ fastForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ slowForkingCpmTestVersion' (knownChainGraph g) undefined + , [ slowForkingCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ barebonesTestVersion' (knownChainGraph g) undefined + , [ barebonesTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ noBridgeCpmTestVersion' (knownChainGraph g) undefined + , [ noBridgeCpmTestVersion (knownChainGraph g) | g :: KnownGraph <- [minBound..maxBound] ] - , [ _versionName $ timedConsensusVersion' (knownChainGraph g1) (knownChainGraph g2) undefined + , [ timedConsensusVersion (knownChainGraph g1) (knownChainGraph g2) | g1 :: KnownGraph <- [minBound..maxBound] , g2 :: KnownGraph <- [minBound..maxBound] ] ] +-- | Details common to all test versions thus far. +-- Using this, a `ChainwebVersion`'s `versionCode` is set to the version's +-- index in `testVersions`, to ensure that all test versions have unique codes +-- in the global version registry in `Chainweb.Version.Registry`. testVersionTemplate :: VersionBuilder testVersionTemplate v = v - & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.findIndex (\vn -> vn == _versionName v) testRegistry) + 0x80000000) + & versionCode .~ ChainwebVersionCode (int (fromJuste $ List.elemIndex (_versionName v) testVersions) + 0x80000000) & versionHeaderBaseSizeBytes .~ 318 - 110 & versionWindow .~ WindowWidth 120 & versionMaxBlockGasLimit .~ End (Just 2_000_000) & versionBootstraps .~ [testBootstrapPeerInfos] +-- | A set of fork heights which are relatively fast, but not fast enough to break anything. fastForks :: HashMap Fork (ChainMap BlockHeight) fastForks = tabulateHashMap $ \case Pact420 -> AllChains (BlockHeight 0) @@ -129,11 +136,9 @@ fastForks = tabulateHashMap $ \case Chainweb217Pact -> AllChains (BlockHeight 20) Chainweb218Pact -> AllChains (BlockHeight 21) +-- | A test version without Pact or PoW, with only one chain graph. barebonesTestVersion :: ChainGraph -> ChainwebVersion -barebonesTestVersion g = legalizeTestVersion (barebonesTestVersion' g) - -barebonesTestVersion' :: ChainGraph -> VersionBuilder -barebonesTestVersion' g v = +barebonesTestVersion g = buildTestVersion $ \v -> testVersionTemplate v & versionWindow .~ WindowWidth 120 & versionBlockRate .~ BlockRate 1_000_000 @@ -156,8 +161,41 @@ barebonesTestVersion' g v = & versionForks .~ HM.fromList [ (f, AllChains $ BlockHeight 0) | f <- [minBound..maxBound] ] & versionUpgrades .~ AllChains HM.empty +-- | A test version without Pact or PoW, with a chain graph upgrade at block height 8. +timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion +timedConsensusVersion g1 g2 = buildTestVersion $ \v -> v + & testVersionTemplate + & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) + & versionBlockRate .~ BlockRate 1_000_000 + & versionWindow .~ WindowWidth 120 + & versionForks .~ tabulateHashMap (\case + SkipTxTimingValidation -> AllChains (BlockHeight 2) + -- pact is disabled, we don't care about pact forks + _ -> AllChains (BlockHeight 0) + ) + & versionUpgrades .~ AllChains HM.empty + & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) + & versionCheats .~ VersionCheats + { _disablePow = True + , _fakeFirstEpochStart = True + , _disablePact = True + } + & versionDefaults .~ VersionDefaults + { _disableMempoolSync = True + , _disablePeerValidation = True + } + & versionGenesis .~ VersionGenesis + { _genesisBlockPayload = onChains $ + (unsafeChainId 0, TN0.payloadBlock) : + [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] + , _genesisBlockTarget = AllChains maxTarget + , _genesisTime = AllChains $ BlockCreationTime epoch + } + +-- | A family of versions each with Pact enabled and PoW disabled. cpmTestVersion :: ChainGraph -> VersionBuilder cpmTestVersion g v = v + & testVersionTemplate & versionWindow .~ WindowWidth 120 & versionBlockRate .~ BlockRate (Micros 100_000) & versionGraphs .~ End g @@ -186,88 +224,49 @@ cpmTestVersion g v = v ]) (onChains [(unsafeChainId 3, HM.singleton (BlockHeight 2) (Upgrade MNKAD.transactions False))]) +-- | CPM version (see `cpmTestVersion`) with forks and upgrades slowly enabled. slowForkingCpmTestVersion :: ChainGraph -> ChainwebVersion -slowForkingCpmTestVersion g = legalizeTestVersion (slowForkingCpmTestVersion' g) - -slowForkingCpmTestVersion' :: ChainGraph -> VersionBuilder -slowForkingCpmTestVersion' g v = - cpmTestVersion g (testVersionTemplate v) - & versionName .~ ChainwebVersionName ("slowfork-CPM-" <> toText g) - & versionForks .~ HM.fromList - [ (SlowEpoch, AllChains (BlockHeight 0)) - , (OldTargetGuard, AllChains (BlockHeight 0)) - , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) - , (OldDAGuard, AllChains (BlockHeight 0)) - , (Vuln797Fix, AllChains (BlockHeight 0)) - , (PactBackCompat_v16, AllChains (BlockHeight 0)) - , (SPVBridge, AllChains (BlockHeight 0)) - , (Pact44NewTrans, AllChains (BlockHeight 0)) - , (CoinV2, AllChains (BlockHeight 1)) - , (SkipTxTimingValidation, AllChains (BlockHeight 2)) - , (ModuleNameFix, AllChains (BlockHeight 2)) - , (ModuleNameFix2, AllChains (BlockHeight 2)) - , (Pact420, AllChains (BlockHeight 5)) - , (CheckTxHash, AllChains (BlockHeight 7)) - , (EnforceKeysetFormats, AllChains (BlockHeight 10)) - , (PactEvents, AllChains (BlockHeight 10)) - , (Pact4Coin3, AllChains (BlockHeight 20)) - , (Chainweb213Pact, AllChains (BlockHeight 26)) - , (Chainweb214Pact, AllChains (BlockHeight 30)) - , (Chainweb215Pact, AllChains (BlockHeight 35)) - , (Chainweb216Pact, AllChains (BlockHeight 53)) - , (Chainweb217Pact, AllChains (BlockHeight 55)) - , (Chainweb218Pact, AllChains (BlockHeight 60)) - ] +slowForkingCpmTestVersion g = buildTestVersion $ \v -> v + & cpmTestVersion g + & versionName .~ ChainwebVersionName ("slowfork-CPM-" <> toText g) + & versionForks .~ HM.fromList + [ (SlowEpoch, AllChains (BlockHeight 0)) + , (OldTargetGuard, AllChains (BlockHeight 0)) + , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) + , (OldDAGuard, AllChains (BlockHeight 0)) + , (Vuln797Fix, AllChains (BlockHeight 0)) + , (PactBackCompat_v16, AllChains (BlockHeight 0)) + , (SPVBridge, AllChains (BlockHeight 0)) + , (Pact44NewTrans, AllChains (BlockHeight 0)) + , (CoinV2, AllChains (BlockHeight 1)) + , (SkipTxTimingValidation, AllChains (BlockHeight 2)) + , (ModuleNameFix, AllChains (BlockHeight 2)) + , (ModuleNameFix2, AllChains (BlockHeight 2)) + , (Pact420, AllChains (BlockHeight 5)) + , (CheckTxHash, AllChains (BlockHeight 7)) + , (EnforceKeysetFormats, AllChains (BlockHeight 10)) + , (PactEvents, AllChains (BlockHeight 10)) + , (Pact4Coin3, AllChains (BlockHeight 20)) + , (Chainweb213Pact, AllChains (BlockHeight 26)) + , (Chainweb214Pact, AllChains (BlockHeight 30)) + , (Chainweb215Pact, AllChains (BlockHeight 35)) + , (Chainweb216Pact, AllChains (BlockHeight 53)) + , (Chainweb217Pact, AllChains (BlockHeight 55)) + , (Chainweb218Pact, AllChains (BlockHeight 60)) + ] +-- | CPM version (see `cpmTestVersion`) with forks and upgrades quickly enabled. fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion -fastForkingCpmTestVersion g = legalizeTestVersion (fastForkingCpmTestVersion' g) - -fastForkingCpmTestVersion' :: ChainGraph -> VersionBuilder -fastForkingCpmTestVersion' g v = - cpmTestVersion g (testVersionTemplate v) - & versionName .~ ChainwebVersionName ("fastfork-CPM-" <> toText g) - & versionForks .~ fastForks +fastForkingCpmTestVersion g = buildTestVersion $ \v -> v + & cpmTestVersion g + & versionName .~ ChainwebVersionName ("fastfork-CPM-" <> toText g) + & versionForks .~ fastForks +-- | CPM version (see `cpmTestVersion`) with forks and upgrades quickly enabled +-- but with no SPV bridge. noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion -noBridgeCpmTestVersion g = legalizeTestVersion (noBridgeCpmTestVersion' g) - -noBridgeCpmTestVersion' :: ChainGraph -> VersionBuilder -noBridgeCpmTestVersion' g v = - cpmTestVersion g (testVersionTemplate v) - & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) - & versionForks .~ (fastForks & at SPVBridge ?~ AllChains maxBound) - -timedConsensusVersion :: ChainGraph -> ChainGraph -> ChainwebVersion -timedConsensusVersion g1 g2 = legalizeTestVersion (timedConsensusVersion' g1 g2) - -timedConsensusVersion' :: ChainGraph -> ChainGraph -> VersionBuilder -timedConsensusVersion' g1 g2 v = - testVersionTemplate v - & versionName .~ ChainwebVersionName ("timedConsensus-" <> toText g1 <> "-" <> toText g2) - & versionBlockRate .~ BlockRate 1_000_000 - & versionWindow .~ WindowWidth 120 - & versionForks .~ tabulateHashMap (\case - SkipTxTimingValidation -> AllChains (BlockHeight 2) - -- pact is disabled, we don't care about pact forks - _ -> AllChains (BlockHeight 0) - ) - & versionUpgrades .~ AllChains HM.empty - & versionWindow .~ WindowWidth 120 - & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) - & versionCheats .~ VersionCheats - { _disablePow = True - , _fakeFirstEpochStart = True - , _disablePact = True - } - & versionDefaults .~ VersionDefaults - { _disableMempoolSync = True - , _disablePeerValidation = True - } - & versionGenesis .~ VersionGenesis - { _genesisBlockPayload = onChains $ - (unsafeChainId 0, TN0.payloadBlock) : - [(n, TNN.payloadBlock) | n <- HS.toList (unsafeChainId 0 `HS.delete` chainIds v)] - , _genesisBlockTarget = AllChains maxTarget - , _genesisTime = AllChains $ BlockCreationTime epoch - } +noBridgeCpmTestVersion g = buildTestVersion $ \v -> v + & cpmTestVersion g + & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) + & versionForks .~ (fastForks & at SPVBridge ?~ AllChains maxBound) From d3025e56955a5f3098bbc3293e9224418c8852c3 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 15:37:00 -0400 Subject: [PATCH 42/91] Small fork height tweaks --- src/Chainweb/Version/Development.hs | 52 ++++++++----------- .../Test/Pact/ModuleCacheOnRestart.hs | 2 +- test/Chainweb/Test/TestVersions.hs | 4 +- 3 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 0e0a56b75d..bad1a3bb5d 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -41,39 +41,33 @@ devnet = ChainwebVersion , _versionName = ChainwebVersionName "development" , _versionForks = tabulateHashMap $ \case - SlowEpoch -> AllChains 0 - Vuln797Fix -> AllChains 0 - CoinV2 -> onChains $ concat - [ [(unsafeChainId 0, BlockHeight 3)] - , [(unsafeChainId i, BlockHeight 4) | i <- [1..19]] - ] - PactBackCompat_v16 -> AllChains 1 - ModuleNameFix -> AllChains 1 - SkipTxTimingValidation -> AllChains 1 - OldTargetGuard -> AllChains 0 - SkipFeatureFlagValidation -> AllChains 1 - ModuleNameFix2 -> AllChains 1 - OldDAGuard -> AllChains 13 - PactEvents -> AllChains 1 - SPVBridge -> AllChains 1 - Pact4Coin3 -> AllChains $ BlockHeight 14 - EnforceKeysetFormats -> AllChains 1 + SlowEpoch -> AllChains $ BlockHeight 0 + Vuln797Fix -> AllChains $ BlockHeight 0 + CoinV2 -> AllChains $ BlockHeight 1 + PactBackCompat_v16 -> AllChains $ BlockHeight 0 + ModuleNameFix -> AllChains $ BlockHeight 0 + SkipTxTimingValidation -> AllChains $ BlockHeight 0 + OldTargetGuard -> AllChains $ BlockHeight 0 + SkipFeatureFlagValidation -> AllChains $ BlockHeight 0 + ModuleNameFix2 -> AllChains $ BlockHeight 0 + OldDAGuard -> AllChains $ BlockHeight 1 + PactEvents -> AllChains $ BlockHeight 1 + SPVBridge -> AllChains $ BlockHeight 1 + Pact4Coin3 -> AllChains $ BlockHeight 2 + EnforceKeysetFormats -> AllChains $ BlockHeight 1 Pact420 -> AllChains $ BlockHeight 1 - CheckTxHash -> AllChains 1 - Chainweb213Pact -> AllChains $ BlockHeight 15 - Chainweb214Pact -> AllChains $ BlockHeight 16 - Chainweb215Pact -> AllChains $ BlockHeight 17 - Pact44NewTrans -> AllChains 1 - Chainweb216Pact -> AllChains $ BlockHeight 18 - Chainweb217Pact -> AllChains $ BlockHeight 18 - Chainweb218Pact -> AllChains $ BlockHeight 18 + CheckTxHash -> AllChains $ BlockHeight 1 + Chainweb213Pact -> AllChains $ BlockHeight 3 + Chainweb214Pact -> AllChains $ BlockHeight 4 + Chainweb215Pact -> AllChains $ BlockHeight 5 + Pact44NewTrans -> AllChains $ BlockHeight 1 + Chainweb216Pact -> AllChains $ BlockHeight 6 + Chainweb217Pact -> AllChains $ BlockHeight 6 + Chainweb218Pact -> AllChains $ BlockHeight 6 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ forkUpgrades devnet - [ (CoinV2, onChains $ concat - [ [(unsafeChainId 0, upgrade Devnet.transactions)] - , [(unsafeChainId i, upgrade Devnet.transactions) | i <- [1..9]] - ]) + [ (CoinV2, onChains [(unsafeChainId i, upgrade Devnet.transactions) | i <- [0..9]]) , (Pact4Coin3, AllChains (Upgrade CoinV3.transactions True)) , (Chainweb214Pact, AllChains (Upgrade CoinV4.transactions True)) , (Chainweb215Pact, AllChains (Upgrade CoinV5.transactions True)) diff --git a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs index 7270bdbf5d..7a44b1d46e 100644 --- a/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs +++ b/test/Chainweb/Test/Pact/ModuleCacheOnRestart.hs @@ -206,7 +206,7 @@ testCw217CoinOnly iobdb _rewindM = (go, go') where go = do initPayloadState - void $ doNextCoinbaseN_ 9 iobdb + void $ doNextCoinbaseN_ 8 iobdb go' ioa initCache = do snapshotCache ioa initCache diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 14e783eced..b28e1de991 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -132,9 +132,9 @@ fastForks = tabulateHashMap $ \case Pact4Coin3 -> AllChains (BlockHeight 4) Chainweb214Pact -> AllChains (BlockHeight 5) Chainweb215Pact -> AllChains (BlockHeight 10) - Chainweb216Pact -> AllChains (BlockHeight 16) + Chainweb216Pact -> AllChains (BlockHeight 11) Chainweb217Pact -> AllChains (BlockHeight 20) - Chainweb218Pact -> AllChains (BlockHeight 21) + Chainweb218Pact -> AllChains (BlockHeight 20) -- | A test version without Pact or PoW, with only one chain graph. barebonesTestVersion :: ChainGraph -> ChainwebVersion From 217d54d7e4e17ee49c208d3f6e497d9ad66b8567 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 15:53:44 -0400 Subject: [PATCH 43/91] profiling enabled --- cabal.project | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cabal.project b/cabal.project index 49723525c9..a6432f68b1 100644 --- a/cabal.project +++ b/cabal.project @@ -5,6 +5,9 @@ debug-info: True -- -------------------------------------------------------------------------- -- -- Package Specific Build Settings +profiling: True +profiling-detail: all-functions + package chainweb tests: True benchmarks: True From 80bbfe763dbb5d8d90c80011f4e3665f0e74e470 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 17:57:44 -0400 Subject: [PATCH 44/91] fix tests --- test/Chainweb/Test/Pact/TransactionTests.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index ae125a9774..141038d89e 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -124,8 +124,8 @@ tests = testGroup "Chainweb.Test.Pact.TransactionTests" , testGroup "Coinbase Vuln Fix Tests" [ testCoinbase797DateFix , testCase "testCoinbaseEnforceFailure" testCoinbaseEnforceFailure - , testCase "testCoinbaseUpgradeDevnet0" (testCoinbaseUpgradeDevnet (unsafeChainId 0) 3) - , testCase "testCoinbaseUpgradeDevnet1" (testCoinbaseUpgradeDevnet (unsafeChainId 1) 4) + , testCase "testCoinbaseUpgradeDevnet0" (testCoinbaseUpgradeDevnet (unsafeChainId 0) 1) + , testCase "testCoinbaseUpgradeDevnet1" (testCoinbaseUpgradeDevnet (unsafeChainId 1) 1) ] , testGroup "20-Chain Fork Upgrade Tests" [ testTwentyChainDevnetUpgrades From 7e15596c8ba596b92f0af7bd5ad8eb9f7333a4db Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 18:31:59 -0400 Subject: [PATCH 45/91] Hahahahahaha wow. Fix dumb performance glitch --- src/Chainweb/BlockHeader/Validation.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Chainweb/BlockHeader/Validation.hs b/src/Chainweb/BlockHeader/Validation.hs index 43469db35c..628d33cbc3 100644 --- a/src/Chainweb/BlockHeader/Validation.hs +++ b/src/Chainweb/BlockHeader/Validation.hs @@ -798,9 +798,9 @@ prop_block_adjacent_parents (WebStep as (ChainStep _ b)) prop_block_adjacent_parents_version :: WebStep -> Bool prop_block_adjacent_parents_version (WebStep as (ChainStep _ b)) - = all ((== v) . _chainwebVersion . _parentHeader) as + = all ((== v) . _blockChainwebVersion . _parentHeader) as where - v = _chainwebVersion b + v = _blockChainwebVersion b -- | TODO: we don't current check this here. It is enforced in the cut merge -- algorithm , namely in 'monotonicCutExtension'. The property that is checked From ed94cc36d41a77cf3d6c48eb261eff1ce601c46b Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 15 Mar 2023 18:36:14 -0400 Subject: [PATCH 46/91] disable profiling --- cabal.project | 3 --- 1 file changed, 3 deletions(-) diff --git a/cabal.project b/cabal.project index a6432f68b1..49723525c9 100644 --- a/cabal.project +++ b/cabal.project @@ -5,9 +5,6 @@ debug-info: True -- -------------------------------------------------------------------------- -- -- Package Specific Build Settings -profiling: True -profiling-detail: all-functions - package chainweb tests: True benchmarks: True From d37505907ef49d0505e26fc1979ef44bbf948f3e Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 16 Mar 2023 16:37:41 -0400 Subject: [PATCH 47/91] Use ChainwebVersionName in CutHashes --- src/Chainweb/Cut/CutHashes.hs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Chainweb/Cut/CutHashes.hs b/src/Chainweb/Cut/CutHashes.hs index 7fcee36119..a713e7d736 100644 --- a/src/Chainweb/Cut/CutHashes.hs +++ b/src/Chainweb/Cut/CutHashes.hs @@ -44,7 +44,7 @@ module Chainweb.Cut.CutHashes , BlockHashWithHeight(..) , CutHashes(..) , cutHashes -, cutHashesChainwebVersion +, cutHashesChainwebVersionName , cutHashesId , cutOrigin , cutHashesWeight @@ -100,7 +100,6 @@ import Chainweb.Cut import Chainweb.Utils import Chainweb.Utils.Serialization import Chainweb.Version -import Chainweb.Version.Registry import Chainweb.Payload @@ -267,7 +266,7 @@ data CutHashes = CutHashes -- ^ 'Nothing' is used for locally mined Cuts , _cutHashesWeight :: !BlockWeight , _cutHashesHeight :: !CutHeight - , _cutHashesChainwebVersion :: !ChainwebVersion + , _cutHashesChainwebVersionName :: !ChainwebVersionName , _cutHashesId :: !CutId , _cutHashesHeaders :: !(HM.HashMap BlockHash BlockHeader) -- ^ optional block headers @@ -319,7 +318,7 @@ cutHashesProperties c = , "origin" .= _cutOrigin c , "weight" .= _cutHashesWeight c , "height" .= _cutHashesHeight c - , "instance" .= _versionCode (_cutHashesChainwebVersion c) + , "instance" .= _cutHashesChainwebVersionName c , "id" .= _cutHashesId c ] <> ifNotEmpty "headers" cutHashesHeaders @@ -347,7 +346,7 @@ instance FromJSON CutHashes where <*> o .: "origin" <*> o .: "weight" <*> o .: "height" - <*> (lookupVersionByCode <$> o .: "instance") + <*> o .: "instance" <*> o .: "id" <*> o .:? "headers" .!= mempty <*> o .:? "payloads" .!= mempty @@ -361,7 +360,7 @@ cutToCutHashes p c = CutHashes , _cutOrigin = p , _cutHashesWeight = _cutWeight c , _cutHashesHeight = _cutHeight c - , _cutHashesChainwebVersion = _chainwebVersion c + , _cutHashesChainwebVersionName = _versionName $ _chainwebVersion c , _cutHashesId = _cutId c , _cutHashesHeaders = mempty , _cutHashesPayloads = mempty From c0f14f771e53b46cb093e7ac9efe348eba61e739 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 16 Mar 2023 21:54:58 -0400 Subject: [PATCH 48/91] Enable profiling --- cabal.project | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal.project b/cabal.project index 49723525c9..e12fe5d816 100644 --- a/cabal.project +++ b/cabal.project @@ -1,6 +1,8 @@ packages: chainweb.cabal debug-info: True +profiling: True +profiling-detail: all-functions -- -------------------------------------------------------------------------- -- -- Package Specific Build Settings From c694a402bb385dd9c31ff6767862950e535a3c88 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 16 Mar 2023 21:55:00 -0400 Subject: [PATCH 49/91] Revert "Enable profiling" This reverts commit c0f14f771e53b46cb093e7ac9efe348eba61e739. --- cabal.project | 2 -- 1 file changed, 2 deletions(-) diff --git a/cabal.project b/cabal.project index e12fe5d816..49723525c9 100644 --- a/cabal.project +++ b/cabal.project @@ -1,8 +1,6 @@ packages: chainweb.cabal debug-info: True -profiling: True -profiling-detail: all-functions -- -------------------------------------------------------------------------- -- -- Package Specific Build Settings From 08e08aeec9d39678af1bf3f2686854018a8e8a6e Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 24 Mar 2023 18:28:16 -0400 Subject: [PATCH 50/91] fix version configuration and validation --- src/Chainweb/Chainweb/Configuration.hs | 65 +++++++++++++------------- src/Chainweb/Version.hs | 1 + 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 19c9ebd0cd..1de63311d1 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -404,6 +404,15 @@ validateChainwebConfiguration c = do validateBackupConfig (_configBackup c) unless (c ^. chainwebVersion . versionDefaults . disablePeerValidation) $ validateP2pConfiguration (_configP2p c) + validateChainwebVersion (_configChainwebVersion c) + +validateChainwebVersion :: ConfigValidation ChainwebVersion [] +validateChainwebVersion v = unless (_versionCode v == _versionCode devnet) $ + throwError $ T.unwords + [ "Specifying version properties is only legal with chainweb-version" + , "set to development, but version is set to" + , sshow (_versionName v) + ] validateBackupConfig :: ConfigValidation BackupConfig [] validateBackupConfig c = @@ -494,7 +503,7 @@ instance FromJSON (ChainwebConfiguration -> ChainwebConfiguration) where pChainwebConfiguration :: MParser ChainwebConfiguration pChainwebConfiguration = id - <$< configChainwebVersion %:: version + <$< configChainwebVersion %:: parseVersion <*< configHeaderStream .:: boolOption_ % long "header-stream" <> help "whether to enable an endpoint for streaming block updates" @@ -546,39 +555,31 @@ pChainwebConfiguration = id <> help "Maximum size of the per-chain checkpointer module cache in bytes" <> metavar "INT" where - knownVersion = do - option (findKnownVersion =<< textReader) + +parseVersion :: MParser ChainwebVersion +parseVersion = constructVersion + <$> optional + (option (findKnownVersion =<< textReader) % long "chainweb-version" <> short 'v' <> help "the chainweb version that this node is using" - version = constructVersion - <$> optional knownVersion - <*> optional (textOption @Fork (long "fork-upper-bound" <> help "(development mode only) the latest fork the node will enable")) - <*> optional (BlockRate <$> textOption (long "block-rate" <> help "(development mode only) the block rate in seconds per block")) - <*> switch (long "disable-pow" <> help "(development mode only) disable proof of work check") + ) + <*> optional (textOption @Fork (long "fork-upper-bound" <> help "(development mode only) the latest fork the node will enable")) + <*> optional (BlockRate <$> textOption (long "block-rate" <> help "(development mode only) the block rate in seconds per block")) + <*> switch (long "disable-pow" <> help "(development mode only) disable proof of work check") + where + constructVersion cliVersion fub br disablePow' oldVersion = winningVersion + & versionBlockRate .~ fromMaybe (_versionBlockRate winningVersion) br + & versionForks %~ HM.filterWithKey (\fork _ -> fork <= fromMaybe maxBound fub) + & versionUpgrades .~ + maybe (_versionUpgrades winningVersion) (\fub' -> + OnChains $ HM.mapWithKey + (\cid _ -> + let fubHeight = winningVersion ^?! versionForks . at fub' . _Just . onChain cid + in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) + (HS.toMap (chainIds winningVersion)) + ) fub + & versionCheats . disablePow .~ disablePow' where - constructVersion cliVersion fub br disablePow' oldVersion - | _versionCode winningVersion == _versionCode devnet = winningVersion - { _versionBlockRate = fromMaybe (_versionBlockRate winningVersion) br - , _versionForks = - maybe (_versionForks winningVersion) (\fub' -> - HM.filterWithKey (\fork _ -> fork <= fub') (_versionForks winningVersion) - ) fub - , _versionUpgrades = - maybe (_versionUpgrades winningVersion) (\fub' -> - OnChains $ HM.mapWithKey - (\cid _ -> - let fubHeight = winningVersion ^?! versionForks . at fub' . _Just . onChain cid - in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) - (HS.toMap (chainIds winningVersion)) - ) fub - , _versionCheats = - _versionCheats winningVersion & disablePow .~ disablePow' - } - | Nothing <- br, Nothing <- fub = winningVersion - | otherwise = error - $ "Specifying block-rate or fork-upper-bound is only legal with chainweb-version " - <> "set to development, but version is set to " <> show (_versionName winningVersion) - where - winningVersion = fromMaybe oldVersion cliVersion + winningVersion = fromMaybe oldVersion cliVersion diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 0d889feb62..f50cc088c2 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -340,6 +340,7 @@ data ChainwebVersion , _versionCheats :: VersionCheats -- ^ Whether to disable any core functionality. , _versionDefaults :: VersionDefaults + -- ^ Version-specific defaults that can be overridden elsewhere. } deriving stock (Generic) deriving anyclass NFData From d1556c4a64e16d3e799fda1afcae26b2057a411a Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 29 Mar 2023 15:22:21 -0400 Subject: [PATCH 51/91] check --- bench/Bench.hs | 1 + chainweb.cabal | 13 +- pact/coin-contract/v2/coin-install.pact | 583 +++++++++++++++ .../v2/install-coin-contract-v2.yaml | 3 + pact/coin-contract/v5/coin-v5-install.pact | 685 ++++++++++++++++++ .../v5/install-coin-contract-v5.yaml | 3 + ...Payload.hs => Development10to19Payload.hs} | 12 +- ...tNPayload.hs => Development1to9Payload.hs} | 2 +- .../Genesis/FastDevelopment0Payload.hs | 41 ++ .../Genesis/FastDevelopment1to19Payload.hs | 41 ++ ...NPayload.hs => FastTimedCPM1to9Payload.hs} | 2 +- ...tKADPayload.hs => Mainnet10to19Payload.hs} | 2 +- ...tnetNPayload.hs => Testnet1to19Payload.hs} | 2 +- src/Chainweb/Chainweb/Configuration.hs | 10 +- src/Chainweb/Pact/PactService/ExecBlock.hs | 2 +- src/Chainweb/Pact/TransactionExec.hs | 20 +- src/Chainweb/Pact/Types.hs | 3 +- src/Chainweb/Version.hs | 18 +- src/Chainweb/Version/Development.hs | 50 +- src/Chainweb/Version/FastDevelopment.hs | 62 ++ src/Chainweb/Version/Guards.hs | 65 +- src/Chainweb/Version/Mainnet.hs | 90 +-- src/Chainweb/Version/Registry.hs | 8 +- src/Chainweb/Version/Testnet.hs | 52 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 4 +- test/Chainweb/Test/TestVersions.hs | 110 +-- test/ChainwebTests.hs | 2 + tools/cwtool/CwTool.hs | 2 + tools/ea/Ea.hs | 143 ++-- tools/ea/Ea/Genesis.hs | 192 ++--- 30 files changed, 1848 insertions(+), 375 deletions(-) create mode 100644 pact/coin-contract/v2/install-coin-contract-v2.yaml create mode 100644 pact/coin-contract/v5/coin-v5-install.pact create mode 100644 pact/coin-contract/v5/install-coin-contract-v5.yaml rename src/Chainweb/BlockHeader/Genesis/{DevelopmentKADPayload.hs => Development10to19Payload.hs} (98%) rename src/Chainweb/BlockHeader/Genesis/{DevelopmentNPayload.hs => Development1to9Payload.hs} (99%) create mode 100644 src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs create mode 100644 src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs rename src/Chainweb/BlockHeader/Genesis/{FastTimedCPMNPayload.hs => FastTimedCPM1to9Payload.hs} (99%) rename src/Chainweb/BlockHeader/Genesis/{MainnetKADPayload.hs => Mainnet10to19Payload.hs} (99%) rename src/Chainweb/BlockHeader/Genesis/{TestnetNPayload.hs => Testnet1to19Payload.hs} (99%) create mode 100644 src/Chainweb/Version/FastDevelopment.hs diff --git a/bench/Bench.hs b/bench/Bench.hs index 8e501f4e06..98c2f88f8f 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -21,6 +21,7 @@ import Chainweb.Version.Registry main :: IO () main = withTempRocksDb "benchmarks" $ \rdb -> do registerVersion Development + registerVersion FastDevelopment defaultMain [ Checkpointer.bench , ForkingBench.bench rdb diff --git a/chainweb.cabal b/chainweb.cabal index aba15dedc4..485180224a 100644 --- a/chainweb.cabal +++ b/chainweb.cabal @@ -122,12 +122,14 @@ library , Chainweb.BlockHash , Chainweb.BlockHeader , Chainweb.BlockHeader.Genesis.Development0Payload - , Chainweb.BlockHeader.Genesis.DevelopmentKADPayload - , Chainweb.BlockHeader.Genesis.DevelopmentNPayload + , Chainweb.BlockHeader.Genesis.Development1to9Payload + , Chainweb.BlockHeader.Genesis.Development10to19Payload + , Chainweb.BlockHeader.Genesis.FastDevelopment0Payload + , Chainweb.BlockHeader.Genesis.FastDevelopment1to19Payload , Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload - , Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload + , Chainweb.BlockHeader.Genesis.FastTimedCPM1to9Payload , Chainweb.BlockHeader.Genesis.Testnet0Payload - , Chainweb.BlockHeader.Genesis.TestnetNPayload + , Chainweb.BlockHeader.Genesis.Testnet1to19Payload , Chainweb.BlockHeader.Genesis.Mainnet0Payload , Chainweb.BlockHeader.Genesis.Mainnet1Payload , Chainweb.BlockHeader.Genesis.Mainnet2Payload @@ -138,7 +140,7 @@ library , Chainweb.BlockHeader.Genesis.Mainnet7Payload , Chainweb.BlockHeader.Genesis.Mainnet8Payload , Chainweb.BlockHeader.Genesis.Mainnet9Payload - , Chainweb.BlockHeader.Genesis.MainnetKADPayload + , Chainweb.BlockHeader.Genesis.Mainnet10to19Payload , Chainweb.BlockHeader.Validation , Chainweb.BlockHeaderDB , Chainweb.BlockHeaderDB.Internal @@ -237,6 +239,7 @@ library , Chainweb.Utils.Serialization , Chainweb.Version , Chainweb.Version.Development + , Chainweb.Version.FastDevelopment , Chainweb.Version.Guards , Chainweb.Version.Mainnet , Chainweb.Version.Registry diff --git a/pact/coin-contract/v2/coin-install.pact b/pact/coin-contract/v2/coin-install.pact index 1073121052..196a4a729e 100644 --- a/pact/coin-contract/v2/coin-install.pact +++ b/pact/coin-contract/v2/coin-install.pact @@ -1,3 +1,586 @@ +(module coin GOVERNANCE + + @doc "'coin' represents the Kadena Coin Contract. This contract provides both the \ + \buy/redeem gas support in the form of 'fund-tx', as well as transfer, \ + \credit, debit, coinbase, account creation and query, as well as SPV burn \ + \create. To access the coin contract, you may use its fully-qualified name, \ + \or issue the '(use coin)' command in the body of a module declaration." + + @model + [ (defproperty conserves-mass + (= (column-delta coin-table 'balance) 0.0)) + + (defproperty valid-account (account:string) + (and + (>= (length account) 3) + (<= (length account) 256))) + ] + + (implements fungible-v2) + + ; -------------------------------------------------------------------------- + ; Schemas and Tables + + (defschema coin-schema + @doc "The coin contract token schema" + @model [ (invariant (>= balance 0.0)) ] + + balance:decimal + guard:guard) + + (deftable coin-table:{coin-schema}) + + ; -------------------------------------------------------------------------- + ; Capabilities + + (defcap GOVERNANCE () + (enforce false "Enforce non-upgradeability")) + + (defcap GAS () + "Magic capability to protect gas buy and redeem" + true) + + (defcap COINBASE () + "Magic capability to protect miner reward" + true) + + (defcap GENESIS () + "Magic capability constraining genesis transactions" + true) + + (defcap REMEDIATE () + "Magic capability for remediation transactions" + true) + + (defcap DEBIT (sender:string) + "Capability for managing debiting operations" + (enforce-guard (at 'guard (read coin-table sender))) + (enforce (!= sender "") "valid sender")) + + (defcap CREDIT (receiver:string) + "Capability for managing crediting operations" + (enforce (!= receiver "") "valid receiver")) + + (defcap ROTATE (account:string) + @doc "Autonomously managed capability for guard rotation" + @managed + true) + + (defcap TRANSFER:bool + ( sender:string + receiver:string + amount:decimal + ) + @managed amount TRANSFER-mgr + (enforce (!= sender receiver) "same sender and receiver") + (enforce-unit amount) + (enforce (> amount 0.0) "Positive amount") + (compose-capability (DEBIT sender)) + (compose-capability (CREDIT receiver)) + ) + + (defun TRANSFER-mgr:decimal + ( managed:decimal + requested:decimal + ) + + (let ((newbal (- managed requested))) + (enforce (>= newbal 0.0) + (format "TRANSFER exceeded for balance {}" [managed])) + newbal) + ) + + ; -------------------------------------------------------------------------- + ; Constants + + (defconst COIN_CHARSET CHARSET_LATIN1 + "The default coin contract character set") + + (defconst MINIMUM_PRECISION 12 + "Minimum allowed precision for coin transactions") + + (defconst MINIMUM_ACCOUNT_LENGTH 3 + "Minimum account length admissible for coin accounts") + + (defconst MAXIMUM_ACCOUNT_LENGTH 256 + "Maximum account name length admissible for coin accounts") + + ; -------------------------------------------------------------------------- + ; Utilities + + (defun enforce-unit:bool (amount:decimal) + @doc "Enforce minimum precision allowed for coin transactions" + + (enforce + (= (floor amount MINIMUM_PRECISION) + amount) + (format "Amount violates minimum precision: {}" [amount])) + ) + + (defun validate-account (account:string) + @doc "Enforce that an account name conforms to the coin contract \ + \minimum and maximum length requirements, as well as the \ + \latin-1 character set." + + (enforce + (is-charset COIN_CHARSET account) + (format + "Account does not conform to the coin contract charset: {}" + [account])) + + (let ((account-length (length account))) + + (enforce + (>= account-length MINIMUM_ACCOUNT_LENGTH) + (format + "Account name does not conform to the min length requirement: {}" + [account])) + + (enforce + (<= account-length MAXIMUM_ACCOUNT_LENGTH) + (format + "Account name does not conform to the max length requirement: {}" + [account])) + ) + ) + + ; -------------------------------------------------------------------------- + ; Coin Contract + + (defun gas-only () + "Predicate for gas-only user guards." + (require-capability (GAS))) + + (defun gas-guard (guard:guard) + "Predicate for gas + single key user guards" + (enforce-one + "Enforce either the presence of a GAS cap or keyset" + [ (gas-only) + (enforce-guard guard) + ])) + + (defun buy-gas:string (sender:string total:decimal) + @doc "This function describes the main 'gas buy' operation. At this point \ + \MINER has been chosen from the pool, and will be validated. The SENDER \ + \of this transaction has specified a gas limit LIMIT (maximum gas) for \ + \the transaction, and the price is the spot price of gas at that time. \ + \The gas buy will be executed prior to executing SENDER's code." + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + ] + + (validate-account sender) + + (enforce-unit total) + (enforce (> total 0.0) "gas supply must be a positive quantity") + + (require-capability (GAS)) + (with-capability (DEBIT sender) + (debit sender total)) + ) + + (defun redeem-gas:string (miner:string miner-guard:guard sender:string total:decimal) + @doc "This function describes the main 'redeem gas' operation. At this \ + \point, the SENDER's transaction has been executed, and the gas that \ + \was charged has been calculated. MINER will be credited the gas cost, \ + \and SENDER will receive the remainder up to the limit" + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + (property (valid-account miner)) + ] + + (validate-account sender) + (validate-account miner) + (enforce-unit total) + + (require-capability (GAS)) + (let* + ((fee (read-decimal "fee")) + (refund (- total fee))) + + (enforce-unit fee) + (enforce (>= fee 0.0) + "fee must be a non-negative quantity") + + (enforce (>= refund 0.0) + "refund must be a non-negative quantity") + + ; directly update instead of credit + (with-capability (CREDIT sender) + (if (> refund 0.0) + (with-read coin-table sender + { "balance" := balance } + (update coin-table sender + { "balance": (+ balance refund) })) + + "noop")) + + (with-capability (CREDIT miner) + (if (> fee 0.0) + (credit miner miner-guard fee) + "noop")) + ) + + ) + + (defun create-account:string (account:string guard:guard) + @model [ (property (valid-account account)) ] + + (validate-account account) + + (insert coin-table account + { "balance" : 0.0 + , "guard" : guard + }) + ) + + (defun get-balance:decimal (account:string) + (with-read coin-table account + { "balance" := balance } + balance + ) + ) + + (defun details:object{fungible-v2.account-details} + ( account:string ) + (with-read coin-table account + { "balance" := bal + , "guard" := g } + { "account" : account + , "balance" : bal + , "guard": g }) + ) + + (defun rotate:string (account:string new-guard:guard) + (with-capability (ROTATE account) + (with-read coin-table account + { "guard" := old-guard } + + (enforce-guard old-guard) + + (update coin-table account + { "guard" : new-guard } + ))) + ) + + + (defun precision:integer + () + MINIMUM_PRECISION) + + (defun transfer:string (sender:string receiver:string amount:decimal) + @model [ (property conserves-mass) + (property (> amount 0.0)) + (property (valid-account sender)) + (property (valid-account receiver)) + (property (!= sender receiver)) ] + + (enforce (!= sender receiver) + "sender cannot be the receiver of a transfer") + + (validate-account sender) + (validate-account receiver) + + (enforce (> amount 0.0) + "transfer amount must be positive") + + (enforce-unit amount) + + (with-capability (TRANSFER sender receiver amount) + (debit sender amount) + (with-read coin-table receiver + { "guard" := g } + + (credit receiver g amount)) + ) + ) + + (defun transfer-create:string + ( sender:string + receiver:string + receiver-guard:guard + amount:decimal ) + + @model [ (property conserves-mass) ] + + (enforce (!= sender receiver) + "sender cannot be the receiver of a transfer") + + (validate-account sender) + (validate-account receiver) + + (enforce (> amount 0.0) + "transfer amount must be positive") + + (enforce-unit amount) + + (with-capability (TRANSFER sender receiver amount) + (debit sender amount) + (credit receiver receiver-guard amount)) + ) + + (defun coinbase:string (account:string account-guard:guard amount:decimal) + @doc "Internal function for the initial creation of coins. This function \ + \cannot be used outside of the coin contract." + + @model [ (property (valid-account account)) + (property (> amount 0.0)) + ] + + (validate-account account) + (enforce-unit amount) + + (require-capability (COINBASE)) + (with-capability (CREDIT account) + (credit account account-guard amount)) + ) + + (defun remediate:string (account:string amount:decimal) + @doc "Allows for remediation transactions. This function \ + \is protected by the REMEDIATE capability" + @model [ (property (valid-account account)) + (property (> amount 0.0)) + ] + + (validate-account account) + + (enforce (> amount 0.0) + "Remediation amount must be positive") + + (enforce-unit amount) + + (require-capability (REMEDIATE)) + (with-read coin-table account + { "balance" := balance } + + (enforce (<= amount balance) "Insufficient funds") + + (update coin-table account + { "balance" : (- balance amount) } + )) + ) + + (defpact fund-tx (sender:string miner:string miner-guard:guard total:decimal) + @doc "'fund-tx' is a special pact to fund a transaction in two steps, \ + \with the actual transaction transpiring in the middle: \ + \ \ + \ 1) A buying phase, debiting the sender for total gas and fee, yielding \ + \ TX_MAX_CHARGE. \ + \ 2) A settlement phase, resuming TX_MAX_CHARGE, and allocating to the \ + \ coinbase account for used gas and fee, and sender account for bal- \ + \ ance (unused gas, if any)." + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + (property (valid-account miner)) + ;(property conserves-mass) not supported yet + ] + + (step (buy-gas sender total)) + (step (redeem-gas miner miner-guard sender total)) + ) + + (defun debit:string (account:string amount:decimal) + @doc "Debit AMOUNT from ACCOUNT balance" + + @model [ (property (> amount 0.0)) + (property (valid-account account)) + ] + + (validate-account account) + + (enforce (> amount 0.0) + "debit amount must be positive") + + (enforce-unit amount) + + (require-capability (DEBIT account)) + (with-read coin-table account + { "balance" := balance } + + (enforce (<= amount balance) "Insufficient funds") + + (update coin-table account + { "balance" : (- balance amount) } + )) + ) + + + (defun credit:string (account:string guard:guard amount:decimal) + @doc "Credit AMOUNT to ACCOUNT balance" + + @model [ (property (> amount 0.0)) + (property (valid-account account)) + ] + + (validate-account account) + + (enforce (> amount 0.0) "credit amount must be positive") + (enforce-unit amount) + + (require-capability (CREDIT account)) + (with-default-read coin-table account + { "balance" : 0.0, "guard" : guard } + { "balance" := balance, "guard" := retg } + ; we don't want to overwrite an existing guard with the user-supplied one + (enforce (= retg guard) + "account guards do not match") + + (write coin-table account + { "balance" : (+ balance amount) + , "guard" : retg + }) + )) + + + (defschema crosschain-schema + @doc "Schema for yielded value in cross-chain transfers" + receiver:string + receiver-guard:guard + amount:decimal) + + (defpact transfer-crosschain:string + ( sender:string + receiver:string + receiver-guard:guard + target-chain:string + amount:decimal ) + + @model [ (property (> amount 0.0)) + (property (valid-account sender)) + (property (valid-account receiver)) + ] + + (step + (with-capability (DEBIT sender) + + (validate-account sender) + (validate-account receiver) + + (enforce (!= "" target-chain) "empty target-chain") + (enforce (!= (at 'chain-id (chain-data)) target-chain) + "cannot run cross-chain transfers to the same chain") + + (enforce (> amount 0.0) + "transfer quantity must be positive") + + (enforce-unit amount) + + ;; step 1 - debit delete-account on current chain + (debit sender amount) + + (let + ((crosschain-details:object{crosschain-schema} + { "receiver" : receiver + , "receiver-guard" : receiver-guard + , "amount" : amount + })) + (yield crosschain-details target-chain) + ))) + + (step + (resume + { "receiver" := receiver + , "receiver-guard" := receiver-guard + , "amount" := amount + } + + ;; step 2 - credit create account on target chain + (with-capability (CREDIT receiver) + (credit receiver receiver-guard amount)) + )) + ) + + + ; -------------------------------------------------------------------------- + ; Coin allocations + + (defschema allocation-schema + @doc "Genesis allocation registry" + ;@model [ (invariant (>= balance 0.0)) ] + + balance:decimal + date:time + guard:guard + redeemed:bool) + + (deftable allocation-table:{allocation-schema}) + + (defun create-allocation-account + ( account:string + date:time + keyset-ref:string + amount:decimal + ) + + @doc "Add an entry to the coin allocation table. This function \ + \also creates a corresponding empty coin contract account \ + \of the same name and guard. Requires GENESIS capability. " + + @model [ (property (valid-account account)) ] + + (require-capability (GENESIS)) + + (validate-account account) + (enforce (>= amount 0.0) + "allocation amount must be non-negative") + + (enforce-unit amount) + + (let + ((guard:guard (keyset-ref-guard keyset-ref))) + + (create-account account guard) + + (insert allocation-table account + { "balance" : amount + , "date" : date + , "guard" : guard + , "redeemed" : false + }))) + + (defun release-allocation + ( account:string ) + + @doc "Release funds associated with allocation ACCOUNT into main ledger. \ + \ACCOUNT must already exist in main ledger. Allocation is deactivated \ + \after release." + @model [ (property (valid-account account)) ] + + (validate-account account) + + (with-read allocation-table account + { "balance" := balance + , "date" := release-time + , "redeemed" := redeemed + , "guard" := guard + } + + (let ((curr-time:time (at 'block-time (chain-data)))) + + (enforce (not redeemed) + "allocation funds have already been redeemed") + + (enforce + (>= curr-time release-time) + (format "funds locked until {}. current time: {}" [release-time curr-time])) + + (enforce-guard guard) + + (with-capability (CREDIT account) + (credit account guard balance) + + (update allocation-table account + { "redeemed" : true + , "balance" : 0.0 + }) + + "Allocation successfully released to main ledger") + ))) + +) (create-table coin-table) (create-table allocation-table) (enforce diff --git a/pact/coin-contract/v2/install-coin-contract-v2.yaml b/pact/coin-contract/v2/install-coin-contract-v2.yaml new file mode 100644 index 0000000000..a89a4610db --- /dev/null +++ b/pact/coin-contract/v2/install-coin-contract-v2.yaml @@ -0,0 +1,3 @@ +codeFile: coin-install.pact +nonce: coin-contract-v2-temp +keyPairs: [] \ No newline at end of file diff --git a/pact/coin-contract/v5/coin-v5-install.pact b/pact/coin-contract/v5/coin-v5-install.pact new file mode 100644 index 0000000000..bd64448038 --- /dev/null +++ b/pact/coin-contract/v5/coin-v5-install.pact @@ -0,0 +1,685 @@ + +(module coin GOVERNANCE + + @doc "'coin' represents the Kadena Coin Contract. This contract provides both the \ + \buy/redeem gas support in the form of 'fund-tx', as well as transfer, \ + \credit, debit, coinbase, account creation and query, as well as SPV burn \ + \create. To access the coin contract, you may use its fully-qualified name, \ + \or issue the '(use coin)' command in the body of a module declaration." + + @model + [ (defproperty conserves-mass + (= (column-delta coin-table 'balance) 0.0)) + + (defproperty valid-account (account:string) + (and + (>= (length account) 3) + (<= (length account) 256))) + ] + + (implements fungible-v2) + (implements fungible-xchain-v1) + + ;; coin-v2 + (bless "ut_J_ZNkoyaPUEJhiwVeWnkSQn9JT9sQCWKdjjVVrWo") + + ;; coin v3 + (bless "1os_sLAUYvBzspn5jjawtRpJWiH1WPfhyNraeVvSIwU") + + ;; coin v4 + (bless "BjZW0T2ac6qE_I5X8GE4fal6tTqjhLTC7my0ytQSxLU") + + ; -------------------------------------------------------------------------- + ; Schemas and Tables + + (defschema coin-schema + @doc "The coin contract token schema" + @model [ (invariant (>= balance 0.0)) ] + + balance:decimal + guard:guard) + + (deftable coin-table:{coin-schema}) + + ; -------------------------------------------------------------------------- + ; Capabilities + + (defcap GOVERNANCE () + (enforce false "Enforce non-upgradeability")) + + (defcap GAS () + "Magic capability to protect gas buy and redeem" + true) + + (defcap COINBASE () + "Magic capability to protect miner reward" + true) + + (defcap GENESIS () + "Magic capability constraining genesis transactions" + true) + + (defcap REMEDIATE () + "Magic capability for remediation transactions" + true) + + (defcap DEBIT (sender:string) + "Capability for managing debiting operations" + (enforce-guard (at 'guard (read coin-table sender))) + (enforce (!= sender "") "valid sender")) + + (defcap CREDIT (receiver:string) + "Capability for managing crediting operations" + (enforce (!= receiver "") "valid receiver")) + + (defcap ROTATE (account:string) + @doc "Autonomously managed capability for guard rotation" + @managed + true) + + (defcap TRANSFER:bool + ( sender:string + receiver:string + amount:decimal + ) + @managed amount TRANSFER-mgr + (enforce (!= sender receiver) "same sender and receiver") + (enforce-unit amount) + (enforce (> amount 0.0) "Positive amount") + (compose-capability (DEBIT sender)) + (compose-capability (CREDIT receiver)) + ) + + (defun TRANSFER-mgr:decimal + ( managed:decimal + requested:decimal + ) + + (let ((newbal (- managed requested))) + (enforce (>= newbal 0.0) + (format "TRANSFER exceeded for balance {}" [managed])) + newbal) + ) + + (defcap TRANSFER_XCHAIN:bool + ( sender:string + receiver:string + amount:decimal + target-chain:string + ) + + @managed amount TRANSFER_XCHAIN-mgr + (enforce-unit amount) + (enforce (> amount 0.0) "Cross-chain transfers require a positive amount") + (compose-capability (DEBIT sender)) + ) + + (defun TRANSFER_XCHAIN-mgr:decimal + ( managed:decimal + requested:decimal + ) + + (enforce (>= managed requested) + (format "TRANSFER_XCHAIN exceeded for balance {}" [managed])) + 0.0 + ) + + (defcap TRANSFER_XCHAIN_RECD:bool + ( sender:string + receiver:string + amount:decimal + source-chain:string + ) + @event true + ) + + ; v3 capabilities + (defcap RELEASE_ALLOCATION + ( account:string + amount:decimal + ) + @doc "Event for allocation release, can be used for sig scoping." + @event true + ) + + ; -------------------------------------------------------------------------- + ; Constants + + (defconst COIN_CHARSET CHARSET_LATIN1 + "The default coin contract character set") + + (defconst MINIMUM_PRECISION 12 + "Minimum allowed precision for coin transactions") + + (defconst MINIMUM_ACCOUNT_LENGTH 3 + "Minimum account length admissible for coin accounts") + + (defconst MAXIMUM_ACCOUNT_LENGTH 256 + "Maximum account name length admissible for coin accounts") + + (defconst VALID_CHAIN_IDS (map (int-to-str 10) (enumerate 0 19)) + "List of all valid Chainweb chain ids") + + ; -------------------------------------------------------------------------- + ; Utilities + + (defun enforce-unit:bool (amount:decimal) + @doc "Enforce minimum precision allowed for coin transactions" + + (enforce + (= (floor amount MINIMUM_PRECISION) + amount) + (format "Amount violates minimum precision: {}" [amount])) + ) + + (defun validate-account (account:string) + @doc "Enforce that an account name conforms to the coin contract \ + \minimum and maximum length requirements, as well as the \ + \latin-1 character set." + + (enforce + (is-charset COIN_CHARSET account) + (format + "Account does not conform to the coin contract charset: {}" + [account])) + + (let ((account-length (length account))) + + (enforce + (>= account-length MINIMUM_ACCOUNT_LENGTH) + (format + "Account name does not conform to the min length requirement: {}" + [account])) + + (enforce + (<= account-length MAXIMUM_ACCOUNT_LENGTH) + (format + "Account name does not conform to the max length requirement: {}" + [account])) + ) + ) + + ; -------------------------------------------------------------------------- + ; Coin Contract + + (defun gas-only () + "Predicate for gas-only user guards." + (require-capability (GAS))) + + (defun gas-guard (guard:guard) + "Predicate for gas + single key user guards" + (enforce-one + "Enforce either the presence of a GAS cap or keyset" + [ (gas-only) + (enforce-guard guard) + ])) + + (defun buy-gas:string (sender:string total:decimal) + @doc "This function describes the main 'gas buy' operation. At this point \ + \MINER has been chosen from the pool, and will be validated. The SENDER \ + \of this transaction has specified a gas limit LIMIT (maximum gas) for \ + \the transaction, and the price is the spot price of gas at that time. \ + \The gas buy will be executed prior to executing SENDER's code." + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + ] + + (validate-account sender) + + (enforce-unit total) + (enforce (> total 0.0) "gas supply must be a positive quantity") + + (require-capability (GAS)) + (with-capability (DEBIT sender) + (debit sender total)) + ) + + (defun redeem-gas:string (miner:string miner-guard:guard sender:string total:decimal) + @doc "This function describes the main 'redeem gas' operation. At this \ + \point, the SENDER's transaction has been executed, and the gas that \ + \was charged has been calculated. MINER will be credited the gas cost, \ + \and SENDER will receive the remainder up to the limit" + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + (property (valid-account miner)) + ] + + (validate-account sender) + (validate-account miner) + (enforce-unit total) + + (require-capability (GAS)) + (let* + ((fee (read-decimal "fee")) + (refund (- total fee))) + + (enforce-unit fee) + (enforce (>= fee 0.0) + "fee must be a non-negative quantity") + + (enforce (>= refund 0.0) + "refund must be a non-negative quantity") + + (emit-event (TRANSFER sender miner fee)) ;v3 + + ; directly update instead of credit + (with-capability (CREDIT sender) + (if (> refund 0.0) + (with-read coin-table sender + { "balance" := balance } + (update coin-table sender + { "balance": (+ balance refund) })) + + "noop")) + + (with-capability (CREDIT miner) + (if (> fee 0.0) + (credit miner miner-guard fee) + "noop")) + ) + + ) + + (defun create-account:string (account:string guard:guard) + @model [ (property (valid-account account)) ] + + (validate-account account) + (enforce-reserved account guard) + + (insert coin-table account + { "balance" : 0.0 + , "guard" : guard + }) + ) + + (defun get-balance:decimal (account:string) + (with-read coin-table account + { "balance" := balance } + balance + ) + ) + + (defun details:object{fungible-v2.account-details} + ( account:string ) + (with-read coin-table account + { "balance" := bal + , "guard" := g } + { "account" : account + , "balance" : bal + , "guard": g }) + ) + + (defun rotate:string (account:string new-guard:guard) + (with-capability (ROTATE account) + (with-read coin-table account + { "guard" := old-guard } + + (enforce-guard old-guard) + + (update coin-table account + { "guard" : new-guard } + ))) + ) + + + (defun precision:integer + () + MINIMUM_PRECISION) + + (defun transfer:string (sender:string receiver:string amount:decimal) + @model [ (property conserves-mass) + (property (> amount 0.0)) + (property (valid-account sender)) + (property (valid-account receiver)) + (property (!= sender receiver)) ] + + (enforce (!= sender receiver) + "sender cannot be the receiver of a transfer") + + (validate-account sender) + (validate-account receiver) + + (enforce (> amount 0.0) + "transfer amount must be positive") + + (enforce-unit amount) + + (with-capability (TRANSFER sender receiver amount) + (debit sender amount) + (with-read coin-table receiver + { "guard" := g } + + (credit receiver g amount)) + ) + ) + + (defun transfer-create:string + ( sender:string + receiver:string + receiver-guard:guard + amount:decimal ) + + @model [ (property conserves-mass) ] + + (enforce (!= sender receiver) + "sender cannot be the receiver of a transfer") + + (validate-account sender) + (validate-account receiver) + + (enforce (> amount 0.0) + "transfer amount must be positive") + + (enforce-unit amount) + + (with-capability (TRANSFER sender receiver amount) + (debit sender amount) + (credit receiver receiver-guard amount)) + ) + + (defun coinbase:string (account:string account-guard:guard amount:decimal) + @doc "Internal function for the initial creation of coins. This function \ + \cannot be used outside of the coin contract." + + @model [ (property (valid-account account)) + (property (> amount 0.0)) + ] + + (validate-account account) + (enforce-unit amount) + + (require-capability (COINBASE)) + (emit-event (TRANSFER "" account amount)) ;v3 + (with-capability (CREDIT account) + (credit account account-guard amount)) + ) + + (defun remediate:string (account:string amount:decimal) + @doc "Allows for remediation transactions. This function \ + \is protected by the REMEDIATE capability" + @model [ (property (valid-account account)) + (property (> amount 0.0)) + ] + + (validate-account account) + + (enforce (> amount 0.0) + "Remediation amount must be positive") + + (enforce-unit amount) + + (require-capability (REMEDIATE)) + (emit-event (TRANSFER "" account amount)) ;v3 + (with-read coin-table account + { "balance" := balance } + + (enforce (<= amount balance) "Insufficient funds") + + (update coin-table account + { "balance" : (- balance amount) } + )) + ) + + (defpact fund-tx (sender:string miner:string miner-guard:guard total:decimal) + @doc "'fund-tx' is a special pact to fund a transaction in two steps, \ + \with the actual transaction transpiring in the middle: \ + \ \ + \ 1) A buying phase, debiting the sender for total gas and fee, yielding \ + \ TX_MAX_CHARGE. \ + \ 2) A settlement phase, resuming TX_MAX_CHARGE, and allocating to the \ + \ coinbase account for used gas and fee, and sender account for bal- \ + \ ance (unused gas, if any)." + + @model [ (property (> total 0.0)) + (property (valid-account sender)) + (property (valid-account miner)) + ;(property conserves-mass) not supported yet + ] + + (step (buy-gas sender total)) + (step (redeem-gas miner miner-guard sender total)) + ) + + (defun debit:string (account:string amount:decimal) + @doc "Debit AMOUNT from ACCOUNT balance" + + @model [ (property (> amount 0.0)) + (property (valid-account account)) + ] + + (validate-account account) + + (enforce (> amount 0.0) + "debit amount must be positive") + + (enforce-unit amount) + + (require-capability (DEBIT account)) + (with-read coin-table account + { "balance" := balance } + + (enforce (<= amount balance) "Insufficient funds") + + (update coin-table account + { "balance" : (- balance amount) } + )) + ) + + + (defun credit:string (account:string guard:guard amount:decimal) + @doc "Credit AMOUNT to ACCOUNT balance" + + @model [ (property (> amount 0.0)) + (property (valid-account account)) + ] + + (validate-account account) + + (enforce (> amount 0.0) "credit amount must be positive") + (enforce-unit amount) + + (require-capability (CREDIT account)) + (with-default-read coin-table account + { "balance" : -1.0, "guard" : guard } + { "balance" := balance, "guard" := retg } + ; we don't want to overwrite an existing guard with the user-supplied one + (enforce (= retg guard) + "account guards do not match") + + (let ((is-new + (if (= balance -1.0) + (enforce-reserved account guard) + false))) + + (write coin-table account + { "balance" : (if is-new amount (+ balance amount)) + , "guard" : retg + })) + )) + + (defun check-reserved:string (account:string) + " Checks ACCOUNT for reserved name and returns type if \ + \ found or empty string. Reserved names start with a \ + \ single char and colon, e.g. 'c:foo', which would return 'c' as type." + (let ((pfx (take 2 account))) + (if (= ":" (take -1 pfx)) (take 1 pfx) ""))) + + (defun enforce-reserved:bool (account:string guard:guard) + @doc "Enforce reserved account name protocols." + (if (validate-principal guard account) + true + (let ((r (check-reserved account))) + (if (= r "") + true + (if (= r "k") + (enforce false "Single-key account protocol violation") + (enforce false + (format "Reserved protocol guard violation: {}" [r])) + ))))) + + + (defschema crosschain-schema + @doc "Schema for yielded value in cross-chain transfers" + receiver:string + receiver-guard:guard + amount:decimal + source-chain:string) + + (defpact transfer-crosschain:string + ( sender:string + receiver:string + receiver-guard:guard + target-chain:string + amount:decimal ) + + @model [ (property (> amount 0.0)) + (property (valid-account sender)) + (property (valid-account receiver)) + ] + + (step + (with-capability + (TRANSFER_XCHAIN sender receiver amount target-chain) + + (validate-account sender) + (validate-account receiver) + + (enforce (!= "" target-chain) "empty target-chain") + (enforce (!= (at 'chain-id (chain-data)) target-chain) + "cannot run cross-chain transfers to the same chain") + + (enforce (> amount 0.0) + "transfer quantity must be positive") + + (enforce-unit amount) + + (enforce (contains target-chain VALID_CHAIN_IDS) + "target chain is not a valid chainweb chain id") + + ;; step 1 - debit delete-account on current chain + (debit sender amount) + (emit-event (TRANSFER sender "" amount)) + + (let + ((crosschain-details:object{crosschain-schema} + { "receiver" : receiver + , "receiver-guard" : receiver-guard + , "amount" : amount + , "source-chain" : (at 'chain-id (chain-data)) + })) + (yield crosschain-details target-chain) + ))) + + (step + (resume + { "receiver" := receiver + , "receiver-guard" := receiver-guard + , "amount" := amount + , "source-chain" := source-chain + } + + (emit-event (TRANSFER "" receiver amount)) + (emit-event (TRANSFER_XCHAIN_RECD "" receiver amount source-chain)) + + ;; step 2 - credit create account on target chain + (with-capability (CREDIT receiver) + (credit receiver receiver-guard amount)) + )) + ) + + + ; -------------------------------------------------------------------------- + ; Coin allocations + + (defschema allocation-schema + @doc "Genesis allocation registry" + ;@model [ (invariant (>= balance 0.0)) ] + + balance:decimal + date:time + guard:guard + redeemed:bool) + + (deftable allocation-table:{allocation-schema}) + + (defun create-allocation-account + ( account:string + date:time + keyset-ref:string + amount:decimal + ) + + @doc "Add an entry to the coin allocation table. This function \ + \also creates a corresponding empty coin contract account \ + \of the same name and guard. Requires GENESIS capability. " + + @model [ (property (valid-account account)) ] + + (require-capability (GENESIS)) + + (validate-account account) + (enforce (>= amount 0.0) + "allocation amount must be non-negative") + + (enforce-unit amount) + + (let + ((guard:guard (keyset-ref-guard keyset-ref))) + + (create-account account guard) + + (insert allocation-table account + { "balance" : amount + , "date" : date + , "guard" : guard + , "redeemed" : false + }))) + + (defun release-allocation + ( account:string ) + + @doc "Release funds associated with allocation ACCOUNT into main ledger. \ + \ACCOUNT must already exist in main ledger. Allocation is deactivated \ + \after release." + @model [ (property (valid-account account)) ] + + (validate-account account) + + (with-read allocation-table account + { "balance" := balance + , "date" := release-time + , "redeemed" := redeemed + , "guard" := guard + } + + (let ((curr-time:time (at 'block-time (chain-data)))) + + (enforce (not redeemed) + "allocation funds have already been redeemed") + + (enforce + (>= curr-time release-time) + (format "funds locked until {}. current time: {}" [release-time curr-time])) + + (with-capability (RELEASE_ALLOCATION account balance) + + (enforce-guard guard) + + (with-capability (CREDIT account) + (emit-event (TRANSFER "" account balance)) + (credit account guard balance) + + (update allocation-table account + { "redeemed" : true + , "balance" : 0.0 + }) + + "Allocation successfully released to main ledger")) + ))) + +) + +(create-table coin-table) +(create-table allocation-table) \ No newline at end of file diff --git a/pact/coin-contract/v5/install-coin-contract-v5.yaml b/pact/coin-contract/v5/install-coin-contract-v5.yaml new file mode 100644 index 0000000000..12b26ac2b1 --- /dev/null +++ b/pact/coin-contract/v5/install-coin-contract-v5.yaml @@ -0,0 +1,3 @@ +codeFile: coin-v5-install.pact +nonce: coin-contract-v5-install +keyPairs: [] diff --git a/src/Chainweb/BlockHeader/Genesis/DevelopmentKADPayload.hs b/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs similarity index 98% rename from src/Chainweb/BlockHeader/Genesis/DevelopmentKADPayload.hs rename to src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs index 1ce13ddb9c..af17ff18bf 100644 --- a/src/Chainweb/BlockHeader/Genesis/DevelopmentKADPayload.hs +++ b/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs @@ -2,7 +2,7 @@ -- This module is auto-generated. DO NOT EDIT IT MANUALLY. -module Chainweb.BlockHeader.Genesis.DevelopmentKADPayload ( payloadBlock ) where +module Chainweb.BlockHeader.Genesis.Development10to19Payload ( payloadBlock ) where import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T @@ -19,17 +19,17 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , "- - eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjIifSwicmVxS2V5IjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsImxvZ3MiOiJWaGlrLVYzOHByQXRpbHhTV3RZNWYxUnpmVjFUYVJBQzF0N3VVVXZjbGxnIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MX0" , "- - eyJoYXNoIjoieEpIYXlrdmFIMDlQWXpOcVNBTF9FWDlrNU40c2MybUMtdmU2TzlPSTQyWSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuKGNyZWF0ZS10YWJsZSBjb2luLXRhYmxlKVxcbihjcmVhdGUtdGFibGUgYWxsb2NhdGlvbi10YWJsZSlcXG4oZW5mb3JjZVxcbiAgKD1cXG4gICAgXFxcInV0X0pfWk5rb3lhUFVFSmhpd1ZlV25rU1FuOUpUOXNRQ1dLZGpqVlZyV29cXFwiXFxuICAgIChhdCAnaGFzaCAoZGVzY3JpYmUtbW9kdWxlICdjb2luKSkpXFxuICBcXFwiaGFzaCBtaXNtYXRjaFxcXCIpXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyLXRlbXBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6dHJ1ZX0sInJlcUtleSI6InhKSGF5a3ZhSDA5UFl6TnFTQUxfRVg5azVONHNjMm1DLXZlNk85T0k0MlkiLCJsb2dzIjoiTHotbnFTY0FBRTNXdmF6bGhwWnZQVzhyRUxqVHp2ZUZoX1o5TFRXTWluNCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6dHJ1ZX0sInJlcUtleSI6InhKSGF5a3ZhSDA5UFl6TnFTQUxfRVg5azVONHNjMm1DLXZlNk85T0k0MlkiLCJsb2dzIjoiZjVJWUYwQ0todk4zZlRyQkZPWWZEdzNLUE9LUWlQQjlCMG1TQXQyZGdpVSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjJ9" , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjN9" , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZ1BaazFQZzY5UDZUYXlTSEpxZE9QeEthdHc5WTV3eHZXdjNBRFRqMHVRdyIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" , "- - eyJoYXNoIjoiSEhuVGs5N21oUGd6d0gxc0ktMUFrMUljMGQzc3YwNk5ydmpnc2J3SkludyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwic2VuZGVyMDVcIjpbXCJmMDlkOGY2Mzk0YWVhNDI1ZmU2NzgzZDg4Y2Q4MTM2M2Q4MDE3ZjE2YWZkMzcxMWM1NzViZTBmNWNkNWM5YmI5XCJdLFwic2VuZGVyMDRcIjpbXCIyZDcwYWE0ZjY5N2MzYTNiOGRkNmQ5Nzc0NWFjMDc0ZWRjZmQwZWI2NWMzNzc3NGNkZTI1MTM1NDgzYmVhNzFlXCJdLFwibXVsdGktMDItMDMtMDQtYW55XCI6e1wicHJlZFwiOlwia2V5cy1hbnlcIixcImtleXNcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCIsXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCIsXCIyZDcwYWE0ZjY5N2MzYTNiOGRkNmQ5Nzc0NWFjMDc0ZWRjZmQwZWI2NWMzNzc3NGNkZTI1MTM1NDgzYmVhNzFlXCJdfSxcInNlbmRlcjA5XCI6W1wiYzU5ZDk4NDBiMGI2NjA5MDgzNjU0NmI3ZWI0YTczNjA2MjU3NTI3ZWM4YzJiNDgyMzAwZmQyMjkyNjRiMDdlNlwiXSxcImthZC1vcHMtMjBcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIixcImJlMjI5ZjRhOTc1ZTQ0MWRjNjk0ZGVkMGU5MjYwZDk5MzI3MDEyODcwMmZmNWE1YWY3YmVkMmU0MmM5NWNlMDlcIixcIjlhNDg0OTY4N2NiY2ZlYjFmN2M2NTEwNTM5NjM4ZGE1NzYyODk1MDhhZWRjYzc1ZjRkNmFkM2VkMjYyMzYzNWNcIl19LFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJLQURfT1BTXzIwXFxcIiAocmVhZC1rZXlzZXQgXFxcImthZC1vcHMtMjBcXFwiKSAxMC4wKVxcblxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtZ3JhbnRzLWthZG9wc1wifSJ9" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJISG5Uazk3bWhQZ3p3SDFzSS0xQWsxSWMwZDNzdjA2TnJ2amdzYndKSW53IiwibG9ncyI6Ik1XYVZwQmc5TXBwTGtBWll3SHhsVEIwcUl3Q1JuOE85UmdMY2dNdEl6Y3MiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo1fQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJISG5Uazk3bWhQZ3p3SDFzSS0xQWsxSWMwZDNzdjA2TnJ2amdzYndKSW53IiwibG9ncyI6ImR3S0s1UGh3WVlheHBoZHJKc3B1UmFHRnBMWm5lRkh5dFlndDRpTUIySUUiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo1fQ" , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" , "transactionsHash: hjY-SWJcr1XbyfgVAuSgq5O1cP_pMUHzOXK-jOjaryc" - , "outputsHash: lsRFSmvSPahQPqLm1VbTrKYYNOPG2__gBzKsyVG-jcM" - , "payloadHash: OXFBWONFKY7fTY4MH3GaOOELd_cPCMn9GpIOPBYMsvM" + , "outputsHash: 1TnM48xVeQ6-bPJovpJn6QLeey6hTZWKV84QHf2DlmY" + , "payloadHash: UqB64zFBTOSPDTUN4YEwWKdnxqNovSlh3vSAPAxcyp4" , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" , "" ] diff --git a/src/Chainweb/BlockHeader/Genesis/DevelopmentNPayload.hs b/src/Chainweb/BlockHeader/Genesis/Development1to9Payload.hs similarity index 99% rename from src/Chainweb/BlockHeader/Genesis/DevelopmentNPayload.hs rename to src/Chainweb/BlockHeader/Genesis/Development1to9Payload.hs index bce8dd02c0..8749ed88c0 100644 --- a/src/Chainweb/BlockHeader/Genesis/DevelopmentNPayload.hs +++ b/src/Chainweb/BlockHeader/Genesis/Development1to9Payload.hs @@ -2,7 +2,7 @@ -- This module is auto-generated. DO NOT EDIT IT MANUALLY. -module Chainweb.BlockHeader.Genesis.DevelopmentNPayload ( payloadBlock ) where +module Chainweb.BlockHeader.Genesis.Development1to9Payload ( payloadBlock ) where import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T diff --git a/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs b/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs new file mode 100644 index 0000000000..25c0f06c21 --- /dev/null +++ b/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- This module is auto-generated. DO NOT EDIT IT MANUALLY. + +module Chainweb.BlockHeader.Genesis.FastDevelopment0Payload ( payloadBlock ) where + +import Data.Text.Encoding (encodeUtf8) +import qualified Data.Text as T +import Data.Yaml (decodeThrow) + +import Chainweb.Payload (PayloadWithOutputs) +import Chainweb.Utils (fromJuste) + +payloadBlock :: PayloadWithOutputs +payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines + [ "transactions:" + , "- - eyJoYXNoIjoiNDhUMExqQW5TRnBGV3h2dmFQVi1fNkUtQ2pEQVBoV1lVRldidnlmMmxGcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjFcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcbiAgIChkZWZ1biB0cmFuc2Zlci1jcmVhdGU6c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICApXFxuICAgICBAZG9jIFxcXCIgVHJhbnNmZXIgQU1PVU5UIGJldHdlZW4gYWNjb3VudHMgU0VOREVSIGFuZCBSRUNFSVZFUi4gXFxcXFxcbiAgICAgICAgICBcXFxcIEZhaWxzIGlmIFNFTkRFUiBkb2VzIG5vdCBleGlzdC4gSWYgUkVDRUlWRVIgZXhpc3RzLCBndWFyZCBcXFxcXFxuICAgICAgICAgIFxcXFwgbXVzdCBtYXRjaCBleGlzdGluZyB2YWx1ZS4gSWYgUkVDRUlWRVIgZG9lcyBub3QgZXhpc3QsIFxcXFxcXG4gICAgICAgICAgXFxcXCBSRUNFSVZFUiBhY2NvdW50IGlzIGNyZWF0ZWQgdXNpbmcgUkVDRUlWRVItR1VBUkQuIFxcXFxcXG4gICAgICAgICAgXFxcXCBTdWJqZWN0IHRvIG1hbmFnZW1lbnQgYnkgVFJBTlNGRVIgY2FwYWJpbGl0eS5cXFwiXFxuICAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHJlY2VpdmVyIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSlcXG4gICAgICAgICAgICBdXFxuICAgICApXFxuXFxuICAgKGRlZnBhY3QgdHJhbnNmZXItY3Jvc3NjaGFpbjpzdHJpbmdcXG4gICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIDItc3RlcCBwYWN0IHRvIHRyYW5zZmVyIEFNT1VOVCBmcm9tIFNFTkRFUiBvbiBjdXJyZW50IGNoYWluIFxcXFxcXG4gICAgICAgICAgXFxcXCB0byBSRUNFSVZFUiBvbiBUQVJHRVQtQ0hBSU4gdmlhIFNQViBwcm9vZi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFRBUkdFVC1DSEFJTiBtdXN0IGJlIGRpZmZlcmVudCB0aGFuIGN1cnJlbnQgY2hhaW4gaWQuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGaXJzdCBzdGVwIGRlYml0cyBBTU9VTlQgY29pbnMgaW4gU0VOREVSIGFjY291bnQgYW5kIHlpZWxkcyBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIsIFJFQ0VJVkVSX0dVQVJEIGFuZCBBTU9VTlQgdG8gVEFSR0VULUNIQUlOLiBcXFxcXFxuICAgICAgICAgIFxcXFwgU2Vjb25kIHN0ZXAgY29udGludWF0aW9uIGlzIHNlbnQgaW50byBUQVJHRVQtQ0hBSU4gd2l0aCBwcm9vZiBcXFxcXFxuICAgICAgICAgIFxcXFwgb2J0YWluZWQgZnJvbSB0aGUgc3B2ICdvdXRwdXQnIGVuZHBvaW50IG9mIENoYWlud2ViLiBcXFxcXFxuICAgICAgICAgIFxcXFwgUHJvb2YgaXMgdmFsaWRhdGVkIGFuZCBSRUNFSVZFUiBpcyBjcmVkaXRlZCB3aXRoIEFNT1VOVCBcXFxcXFxuICAgICAgICAgIFxcXFwgY3JlYXRpbmcgYWNjb3VudCB3aXRoIFJFQ0VJVkVSX0dVQVJEIGFzIG5lY2Vzc2FyeS5cXFwiXFxuICAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHJlY2VpdmVyIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gdGFyZ2V0LWNoYWluIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICBdXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWxcXG4gICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAgXFxcIiBHZXQgYmFsYW5jZSBmb3IgQUNDT1VOVC4gRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGRldGFpbHM6b2JqZWN0e2FjY291bnQtZGV0YWlsc31cXG4gICAgICggYWNjb3VudDogc3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGFuIG9iamVjdCB3aXRoIGRldGFpbHMgb2YgQUNDT1VOVC4gXFxcXFxcbiAgICAgXFxcXCBGYWlscyBpZiBhY2NvdW50IGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gcHJlY2lzaW9uOmludGVnZXJcXG4gICAgICgpXFxuICAgICBcXFwiUmV0dXJuIHRoZSBtYXhpbXVtIGFsbG93ZWQgZGVjaW1hbCBwcmVjaXNpb24uXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbFxcbiAgICAgKCBhbW91bnQ6ZGVjaW1hbCApXFxuICAgICBcXFwiIEVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgdHJhbnNhY3Rpb25zLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nXFxuICAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgIGd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIENyZWF0ZSBBQ0NPVU5UIHdpdGggMC4wIGJhbGFuY2UsIHdpdGggR1VBUkQgY29udHJvbGxpbmcgYWNjZXNzLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gcm90YXRlOnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBuZXctZ3VhcmQ6Z3VhcmRcXG4gICAgIClcXG4gICAgIFxcXCIgUm90YXRlIGd1YXJkIGZvciBBQ0NPVU5ULiBUcmFuc2FjdGlvbiBpcyB2YWxpZGF0ZWQgYWdhaW5zdCBcXFxcXFxuICAgICBcXFxcIGV4aXN0aW5nIGd1YXJkIGJlZm9yZSBpbnN0YWxsaW5nIG5ldyBndWFyZC4gXFxcIlxcbiAgICAgKVxcblxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImdlbmVzaXMtMDFcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjEifSwicmVxS2V5IjoiNDhUMExqQW5TRnBGV3h2dmFQVi1fNkUtQ2pEQVBoV1lVRldidnlmMmxGcyIsImxvZ3MiOiJRRmEyOHRuOXkydFdMVzdUc3lHdzNOTkhpYzhxYjJ6UGtudXRpWWhnQXc4IiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MH0" + , "- - eyJoYXNoIjoieW5ucDFYVVNSTjJrMUYwYTZ2dXM3RFp0SDZjcHN6MVhmX0d3V0xnTFhTTSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUteGNoYWluLXYxXFxuXFxuICBcXFwiIFRoaXMgaW50ZXJmYWNlIG9mZmVycyBhIHN0YW5kYXJkIGNhcGFiaWxpdHkgZm9yIGNyb3NzLWNoYWluIFxcXFxcXG4gIFxcXFwgdHJhbnNmZXJzIGFuZCBhc3NvY2lhdGVkIGV2ZW50cy4gXFxcIlxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU46Ym9vbFxcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgKVxcbiAgICBAZG9jIFxcXCIgTWFuYWdlZCBjYXBhYmlsaXR5IHNlYWxpbmcgQU1PVU5UIGZvciB0cmFuc2ZlciBcXFxcXFxuICAgICAgICAgXFxcXCBmcm9tIFNFTkRFUiB0byBSRUNFSVZFUiBvbiBUQVJHRVQtQ0hBSU4uIFBlcm1pdHMgXFxcXFxcbiAgICAgICAgIFxcXFwgYW55IG51bWJlciBvZiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdXAgdG8gQU1PVU5ULlxcXCJcXG5cXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSX1hDSEFJTi1tZ3JcXG4gICAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG4gICAgQGRvYyBcXFwiIEFsbG93cyBUUkFOU0ZFUi1YQ0hBSU4gQU1PVU5UIHRvIGJlIGxlc3MgdGhhbiBvciBcXFxcXFxuICAgICAgICAgXFxcXCBlcXVhbCBtYW5hZ2VkIHF1YW50aXR5IGFzIGEgb25lLXNob3QsIHJldHVybmluZyAwLjAuXFxcIlxcbiAgKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU5fUkVDRDpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgc291cmNlLWNoYWluOnN0cmluZ1xcbiAgICApXFxuICAgIEBkb2MgXFxcIkV2ZW50IGVtaXR0ZWQgb24gcmVjZWlwdCBvZiBjcm9zcy1jaGFpbiB0cmFuc2Zlci5cXFwiXFxuICAgIEBldmVudFxcbiAgKVxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImdlbmVzaXMteGNoYWluXCJ9In0" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUteGNoYWluLXYxIn0sInJlcUtleSI6InlubnAxWFVTUk4yazFGMGE2dnVzN0RadEg2Y3BzejFYZl9Hd1dMZ0xYU00iLCJsb2dzIjoiVmRxMHp0SjBnRThOLTNkLW1tRmRRXzZ0ZGp0dnEtNXIwX050dXBvT1hpVSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjF9" + , "- - eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjIifSwicmVxS2V5IjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsImxvZ3MiOiJWaGlrLVYzOHByQXRpbHhTV3RZNWYxUnpmVjFUYVJBQzF0N3VVVXZjbGxnIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6Mn0" + , "- - eyJoYXNoIjoiNHFOMFIzOHdUdDhDdGxlSWg1NXY3WWN0T2IwcnNPblo5bXdNTjZTNHBYOCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG4gIChpbXBsZW1lbnRzIGZ1bmdpYmxlLXhjaGFpbi12MSlcXG5cXG4gIDs7IGNvaW4tdjJcXG4gIChibGVzcyBcXFwidXRfSl9aTmtveWFQVUVKaGl3VmVXbmtTUW45SlQ5c1FDV0tkampWVnJXb1xcXCIpXFxuXFxuICA7OyBjb2luIHYzXFxuICAoYmxlc3MgXFxcIjFvc19zTEFVWXZCenNwbjVqamF3dFJwSldpSDFXUGZoeU5yYWVWdlNJd1VcXFwiKVxcblxcbiAgOzsgY29pbiB2NFxcbiAgKGJsZXNzIFxcXCJCalpXMFQyYWM2cUVfSTVYOEdFNGZhbDZ0VHFqaExUQzdteTB5dFFTeExVXFxcIilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICApXFxuXFxuICAgIEBtYW5hZ2VkIGFtb3VudCBUUkFOU0ZFUl9YQ0hBSU4tbWdyXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiQ3Jvc3MtY2hhaW4gdHJhbnNmZXJzIHJlcXVpcmUgYSBwb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgKGVuZm9yY2UgKD49IG1hbmFnZWQgcmVxdWVzdGVkKVxcbiAgICAgIChmb3JtYXQgXFxcIlRSQU5TRkVSX1hDSEFJTiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgMC4wXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTl9SRUNEOmJvb2xcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgICBzb3VyY2UtY2hhaW46c3RyaW5nXFxuICAgIClcXG4gICAgQGV2ZW50IHRydWVcXG4gIClcXG5cXG4gIDsgdjMgY2FwYWJpbGl0aWVzXFxuICAoZGVmY2FwIFJFTEVBU0VfQUxMT0NBVElPTlxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgKVxcbiAgICBAZG9jIFxcXCJFdmVudCBmb3IgYWxsb2NhdGlvbiByZWxlYXNlLCBjYW4gYmUgdXNlZCBmb3Igc2lnIHNjb3BpbmcuXFxcIlxcbiAgICBAZXZlbnQgdHJ1ZVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb25zdGFudHNcXG5cXG4gIChkZWZjb25zdCBDT0lOX0NIQVJTRVQgQ0hBUlNFVF9MQVRJTjFcXG4gICAgXFxcIlRoZSBkZWZhdWx0IGNvaW4gY29udHJhY3QgY2hhcmFjdGVyIHNldFxcXCIpXFxuXFxuICAoZGVmY29uc3QgTUlOSU1VTV9QUkVDSVNJT04gMTJcXG4gICAgXFxcIk1pbmltdW0gYWxsb3dlZCBwcmVjaXNpb24gZm9yIGNvaW4gdHJhbnNhY3Rpb25zXFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIIDNcXG4gICAgXFxcIk1pbmltdW0gYWNjb3VudCBsZW5ndGggYWRtaXNzaWJsZSBmb3IgY29pbiBhY2NvdW50c1xcXCIpXFxuXFxuICAoZGVmY29uc3QgTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSCAyNTZcXG4gICAgXFxcIk1heGltdW0gYWNjb3VudCBuYW1lIGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBWQUxJRF9DSEFJTl9JRFMgKG1hcCAoaW50LXRvLXN0ciAxMCkgKGVudW1lcmF0ZSAwIDE5KSlcXG4gICAgXFxcIkxpc3Qgb2YgYWxsIHZhbGlkIENoYWlud2ViIGNoYWluIGlkc1xcXCIpXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IFV0aWxpdGllc1xcblxcbiAgKGRlZnVuIGVuZm9yY2UtdW5pdDpib29sIChhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSBtaW5pbXVtIHByZWNpc2lvbiBhbGxvd2VkIGZvciBjb2luIHRyYW5zYWN0aW9uc1xcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoPSAoZmxvb3IgYW1vdW50IE1JTklNVU1fUFJFQ0lTSU9OKVxcbiAgICAgICAgIGFtb3VudClcXG4gICAgICAoZm9ybWF0IFxcXCJBbW91bnQgdmlvbGF0ZXMgbWluaW11bSBwcmVjaXNpb246IHt9XFxcIiBbYW1vdW50XSkpXFxuICAgIClcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZS1hY2NvdW50IChhY2NvdW50OnN0cmluZylcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSB0aGF0IGFuIGFjY291bnQgbmFtZSBjb25mb3JtcyB0byB0aGUgY29pbiBjb250cmFjdCBcXFxcXFxuICAgICAgICAgXFxcXG1pbmltdW0gYW5kIG1heGltdW0gbGVuZ3RoIHJlcXVpcmVtZW50cywgYXMgd2VsbCBhcyB0aGUgICAgXFxcXFxcbiAgICAgICAgIFxcXFxsYXRpbi0xIGNoYXJhY3RlciBzZXQuXFxcIlxcblxcbiAgICAoZW5mb3JjZVxcbiAgICAgIChpcy1jaGFyc2V0IENPSU5fQ0hBUlNFVCBhY2NvdW50KVxcbiAgICAgIChmb3JtYXRcXG4gICAgICAgIFxcXCJBY2NvdW50IGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIGNvaW4gY29udHJhY3QgY2hhcnNldDoge31cXFwiXFxuICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAobGV0ICgoYWNjb3VudC1sZW5ndGggKGxlbmd0aCBhY2NvdW50KSkpXFxuXFxuICAgICAgKGVuZm9yY2VcXG4gICAgICAgICg-PSBhY2NvdW50LWxlbmd0aCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIKVxcbiAgICAgICAgKGZvcm1hdFxcbiAgICAgICAgICBcXFwiQWNjb3VudCBuYW1lIGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIG1pbiBsZW5ndGggcmVxdWlyZW1lbnQ6IHt9XFxcIlxcbiAgICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPD0gYWNjb3VudC1sZW5ndGggTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtYXggbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG4gICAgICApXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gQ29udHJhY3RcXG5cXG4gIChkZWZ1biBnYXMtb25seSAoKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMtb25seSB1c2VyIGd1YXJkcy5cXFwiXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpKVxcblxcbiAgKGRlZnVuIGdhcy1ndWFyZCAoZ3VhcmQ6Z3VhcmQpXFxuICAgIFxcXCJQcmVkaWNhdGUgZm9yIGdhcyArIHNpbmdsZSBrZXkgdXNlciBndWFyZHNcXFwiXFxuICAgIChlbmZvcmNlLW9uZVxcbiAgICAgIFxcXCJFbmZvcmNlIGVpdGhlciB0aGUgcHJlc2VuY2Ugb2YgYSBHQVMgY2FwIG9yIGtleXNldFxcXCJcXG4gICAgICBbIChnYXMtb25seSlcXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIGd1YXJkKVxcbiAgICAgIF0pKVxcblxcbiAgKGRlZnVuIGJ1eS1nYXM6c3RyaW5nIChzZW5kZXI6c3RyaW5nIHRvdGFsOmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIlRoaXMgZnVuY3Rpb24gZGVzY3JpYmVzIHRoZSBtYWluICdnYXMgYnV5JyBvcGVyYXRpb24uIEF0IHRoaXMgcG9pbnQgXFxcXFxcbiAgICBcXFxcTUlORVIgaGFzIGJlZW4gY2hvc2VuIGZyb20gdGhlIHBvb2wsIGFuZCB3aWxsIGJlIHZhbGlkYXRlZC4gVGhlIFNFTkRFUiAgIFxcXFxcXG4gICAgXFxcXG9mIHRoaXMgdHJhbnNhY3Rpb24gaGFzIHNwZWNpZmllZCBhIGdhcyBsaW1pdCBMSU1JVCAobWF4aW11bSBnYXMpIGZvciAgICBcXFxcXFxuICAgIFxcXFx0aGUgdHJhbnNhY3Rpb24sIGFuZCB0aGUgcHJpY2UgaXMgdGhlIHNwb3QgcHJpY2Ugb2YgZ2FzIGF0IHRoYXQgdGltZS4gICAgXFxcXFxcbiAgICBcXFxcVGhlIGdhcyBidXkgd2lsbCBiZSBleGVjdXRlZCBwcmlvciB0byBleGVjdXRpbmcgU0VOREVSJ3MgY29kZS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcblxcbiAgICAoZW5mb3JjZS11bml0IHRvdGFsKVxcbiAgICAoZW5mb3JjZSAoPiB0b3RhbCAwLjApIFxcXCJnYXMgc3VwcGx5IG11c3QgYmUgYSBwb3NpdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcilcXG4gICAgICAoZGViaXQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlZGVlbS1nYXM6c3RyaW5nIChtaW5lcjpzdHJpbmcgbWluZXItZ3VhcmQ6Z3VhcmQgc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAncmVkZWVtIGdhcycgb3BlcmF0aW9uLiBBdCB0aGlzICAgIFxcXFxcXG4gICAgXFxcXHBvaW50LCB0aGUgU0VOREVSJ3MgdHJhbnNhY3Rpb24gaGFzIGJlZW4gZXhlY3V0ZWQsIGFuZCB0aGUgZ2FzIHRoYXQgICAgICBcXFxcXFxuICAgIFxcXFx3YXMgY2hhcmdlZCBoYXMgYmVlbiBjYWxjdWxhdGVkLiBNSU5FUiB3aWxsIGJlIGNyZWRpdGVkIHRoZSBnYXMgY29zdCwgICAgXFxcXFxcbiAgICBcXFxcYW5kIFNFTkRFUiB3aWxsIHJlY2VpdmUgdGhlIHJlbWFpbmRlciB1cCB0byB0aGUgbGltaXRcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBtaW5lcilcXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoR0FTKSlcXG4gICAgKGxldCpcXG4gICAgICAoKGZlZSAocmVhZC1kZWNpbWFsIFxcXCJmZWVcXFwiKSlcXG4gICAgICAgKHJlZnVuZCAoLSB0b3RhbCBmZWUpKSlcXG5cXG4gICAgICAoZW5mb3JjZS11bml0IGZlZSlcXG4gICAgICAoZW5mb3JjZSAoPj0gZmVlIDAuMClcXG4gICAgICAgIFxcXCJmZWUgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgICAgKGVuZm9yY2UgKD49IHJlZnVuZCAwLjApXFxuICAgICAgICBcXFwicmVmdW5kIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgbWluZXIgZmVlKSkgO3YzXFxuXFxuICAgICAgICA7IGRpcmVjdGx5IHVwZGF0ZSBpbnN0ZWFkIG9mIGNyZWRpdFxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBzZW5kZXIpXFxuICAgICAgICAoaWYgKD4gcmVmdW5kIDAuMClcXG4gICAgICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgICAgICAgICh1cGRhdGUgY29pbi10YWJsZSBzZW5kZXJcXG4gICAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiOiAoKyBiYWxhbmNlIHJlZnVuZCkgfSkpXFxuXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuXFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIG1pbmVyKVxcbiAgICAgICAgKGlmICg-IGZlZSAwLjApXFxuICAgICAgICAgIChjcmVkaXQgbWluZXIgbWluZXItZ3VhcmQgZmVlKVxcbiAgICAgICAgICBcXFwibm9vcFxcXCIpKVxcbiAgICAgIClcXG5cXG4gICAgKVxcblxcbiAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgZ3VhcmQ6Z3VhcmQpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS1yZXNlcnZlZCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAoaW5zZXJ0IGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiBndWFyZFxcbiAgICAgIH0pXFxuICAgIClcXG5cXG4gIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsIChhY2NvdW50OnN0cmluZylcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG4gICAgICBiYWxhbmNlXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gZGV0YWlsczpvYmplY3R7ZnVuZ2libGUtdjIuYWNjb3VudC1kZXRhaWxzfVxcbiAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZyB9XFxuICAgICAgeyBcXFwiYWNjb3VudFxcXCIgOiBhY2NvdW50XFxuICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCI6IGcgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJvdGF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIG5ldy1ndWFyZDpndWFyZClcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoUk9UQVRFIGFjY291bnQpXFxuICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBvbGQtZ3VhcmQgfVxcblxcbiAgICAgICAgKGVuZm9yY2UtZ3VhcmQgb2xkLWd1YXJkKVxcblxcbiAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDogbmV3LWd1YXJkIH1cXG4gICAgICAgICAgKSkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBwcmVjaXNpb246aW50ZWdlclxcbiAgICAoKVxcbiAgICBNSU5JTVVNX1BSRUNJU0lPTilcXG5cXG4gIChkZWZ1biB0cmFuc2ZlcjpzdHJpbmcgKHNlbmRlcjpzdHJpbmcgcmVjZWl2ZXI6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgY29uc2VydmVzLW1hc3MpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCByZWNlaXZlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSkgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgcmVjZWl2ZXJcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG5cXG4gICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgZyBhbW91bnQpKVxcbiAgICAgIClcXG4gICAgKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyLWNyZWF0ZTpzdHJpbmdcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICBhbW91bnQ6ZGVjaW1hbCApXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biBjb2luYmFzZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFjY291bnQtZ3VhcmQ6Z3VhcmQgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkludGVybmFsIGZ1bmN0aW9uIGZvciB0aGUgaW5pdGlhbCBjcmVhdGlvbiBvZiBjb2lucy4gIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICBcXFxcY2Fubm90IGJlIHVzZWQgb3V0c2lkZSBvZiB0aGUgY29pbiBjb250cmFjdC5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoQ09JTkJBU0UpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIGFjY291bnQpXFxuICAgICAgKGNyZWRpdCBhY2NvdW50IGFjY291bnQtZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlbWVkaWF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJBbGxvd3MgZm9yIHJlbWVkaWF0aW9uIHRyYW5zYWN0aW9ucy4gVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgICAgICAgXFxcXGlzIHByb3RlY3RlZCBieSB0aGUgUkVNRURJQVRFIGNhcGFiaWxpdHlcXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJSZW1lZGlhdGlvbiBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChSRU1FRElBVEUpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogLTEuMCwgXFxcImd1YXJkXFxcIiA6IGd1YXJkIH1cXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlLCBcXFwiZ3VhcmRcXFwiIDo9IHJldGcgfVxcbiAgICAgIDsgd2UgZG9uJ3Qgd2FudCB0byBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgZ3VhcmQgd2l0aCB0aGUgdXNlci1zdXBwbGllZCBvbmVcXG4gICAgICAoZW5mb3JjZSAoPSByZXRnIGd1YXJkKVxcbiAgICAgICAgXFxcImFjY291bnQgZ3VhcmRzIGRvIG5vdCBtYXRjaFxcXCIpXFxuXFxuICAgICAgKGxldCAoKGlzLW5ld1xcbiAgICAgICAgICAgICAoaWYgKD0gYmFsYW5jZSAtMS4wKVxcbiAgICAgICAgICAgICAgICAgKGVuZm9yY2UtcmVzZXJ2ZWQgYWNjb3VudCBndWFyZClcXG4gICAgICAgICAgICAgICBmYWxzZSkpKVxcblxcbiAgICAgICAgKHdyaXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IChpZiBpcy1uZXcgYW1vdW50ICgrIGJhbGFuY2UgYW1vdW50KSlcXG4gICAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICAgIH0pKVxcbiAgICAgICkpXFxuXFxuICAoZGVmdW4gY2hlY2stcmVzZXJ2ZWQ6c3RyaW5nIChhY2NvdW50OnN0cmluZylcXG4gICAgXFxcIiBDaGVja3MgQUNDT1VOVCBmb3IgcmVzZXJ2ZWQgbmFtZSBhbmQgcmV0dXJucyB0eXBlIGlmIFxcXFxcXG4gICAgXFxcXCBmb3VuZCBvciBlbXB0eSBzdHJpbmcuIFJlc2VydmVkIG5hbWVzIHN0YXJ0IHdpdGggYSBcXFxcXFxuICAgIFxcXFwgc2luZ2xlIGNoYXIgYW5kIGNvbG9uLCBlLmcuICdjOmZvbycsIHdoaWNoIHdvdWxkIHJldHVybiAnYycgYXMgdHlwZS5cXFwiXFxuICAgIChsZXQgKChwZnggKHRha2UgMiBhY2NvdW50KSkpXFxuICAgICAgKGlmICg9IFxcXCI6XFxcIiAodGFrZSAtMSBwZngpKSAodGFrZSAxIHBmeCkgXFxcIlxcXCIpKSlcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXJlc2VydmVkOmJvb2wgKGFjY291bnQ6c3RyaW5nIGd1YXJkOmd1YXJkKVxcbiAgICBAZG9jIFxcXCJFbmZvcmNlIHJlc2VydmVkIGFjY291bnQgbmFtZSBwcm90b2NvbHMuXFxcIlxcbiAgICAoaWYgKHZhbGlkYXRlLXByaW5jaXBhbCBndWFyZCBhY2NvdW50KVxcbiAgICAgIHRydWVcXG4gICAgICAobGV0ICgociAoY2hlY2stcmVzZXJ2ZWQgYWNjb3VudCkpKVxcbiAgICAgICAgKGlmICg9IHIgXFxcIlxcXCIpXFxuICAgICAgICAgIHRydWVcXG4gICAgICAgICAgKGlmICg9IHIgXFxcImtcXFwiKVxcbiAgICAgICAgICAgIChlbmZvcmNlIGZhbHNlIFxcXCJTaW5nbGUta2V5IGFjY291bnQgcHJvdG9jb2wgdmlvbGF0aW9uXFxcIilcXG4gICAgICAgICAgICAoZW5mb3JjZSBmYWxzZVxcbiAgICAgICAgICAgICAgKGZvcm1hdCBcXFwiUmVzZXJ2ZWQgcHJvdG9jb2wgZ3VhcmQgdmlvbGF0aW9uOiB7fVxcXCIgW3JdKSlcXG4gICAgICAgICAgICApKSkpKVxcblxcblxcbiAgKGRlZnNjaGVtYSBjcm9zc2NoYWluLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHlpZWxkZWQgdmFsdWUgaW4gY3Jvc3MtY2hhaW4gdHJhbnNmZXJzXFxcIlxcbiAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgYW1vdW50OmRlY2ltYWxcXG4gICAgc291cmNlLWNoYWluOnN0cmluZylcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5XFxuICAgICAgICAoVFJBTlNGRVJfWENIQUlOIHNlbmRlciByZWNlaXZlciBhbW91bnQgdGFyZ2V0LWNoYWluKVxcblxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoIT0gXFxcIlxcXCIgdGFyZ2V0LWNoYWluKSBcXFwiZW1wdHkgdGFyZ2V0LWNoYWluXFxcIilcXG4gICAgICAgIChlbmZvcmNlICghPSAoYXQgJ2NoYWluLWlkIChjaGFpbi1kYXRhKSkgdGFyZ2V0LWNoYWluKVxcbiAgICAgICAgICBcXFwiY2Fubm90IHJ1biBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdG8gdGhlIHNhbWUgY2hhaW5cXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICAgICAgXFxcInRyYW5zZmVyIHF1YW50aXR5IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoY29udGFpbnMgdGFyZ2V0LWNoYWluIFZBTElEX0NIQUlOX0lEUylcXG4gICAgICAgICAgXFxcInRhcmdldCBjaGFpbiBpcyBub3QgYSB2YWxpZCBjaGFpbndlYiBjaGFpbiBpZFxcXCIpXFxuXFxuICAgICAgICA7OyBzdGVwIDEgLSBkZWJpdCBkZWxldGUtYWNjb3VudCBvbiBjdXJyZW50IGNoYWluXFxuICAgICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgXFxcIlxcXCIgYW1vdW50KSlcXG5cXG4gICAgICAgIChsZXRcXG4gICAgICAgICAgKChjcm9zc2NoYWluLWRldGFpbHM6b2JqZWN0e2Nyb3NzY2hhaW4tc2NoZW1hfVxcbiAgICAgICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6IHJlY2VpdmVyXFxuICAgICAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDogcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDogYW1vdW50XFxuICAgICAgICAgICAgLCBcXFwic291cmNlLWNoYWluXFxcIiA6IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKVxcbiAgICAgICAgICAgIH0pKVxcbiAgICAgICAgICAoeWllbGQgY3Jvc3NjaGFpbi1kZXRhaWxzIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgKSkpXFxuXFxuICAgIChzdGVwXFxuICAgICAgKHJlc3VtZVxcbiAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDo9IHJlY2VpdmVyXFxuICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOj0gcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICwgXFxcImFtb3VudFxcXCIgOj0gYW1vdW50XFxuICAgICAgICAsIFxcXCJzb3VyY2UtY2hhaW5cXFwiIDo9IHNvdXJjZS1jaGFpblxcbiAgICAgICAgfVxcblxcbiAgICAgICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIHJlY2VpdmVyIGFtb3VudCkpXFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVJfWENIQUlOX1JFQ0QgXFxcIlxcXCIgcmVjZWl2ZXIgYW1vdW50IHNvdXJjZS1jaGFpbikpXFxuXFxuICAgICAgICA7OyBzdGVwIDIgLSBjcmVkaXQgY3JlYXRlIGFjY291bnQgb24gdGFyZ2V0IGNoYWluXFxuICAgICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpXFxuICAgICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgQ29pbiBhbGxvY2F0aW9uc1xcblxcbiAgKGRlZnNjaGVtYSBhbGxvY2F0aW9uLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJHZW5lc2lzIGFsbG9jYXRpb24gcmVnaXN0cnlcXFwiXFxuICAgIDtAbW9kZWwgWyAoaW52YXJpYW50ICg-PSBiYWxhbmNlIDAuMCkpIF1cXG5cXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGRhdGU6dGltZVxcbiAgICBndWFyZDpndWFyZFxcbiAgICByZWRlZW1lZDpib29sKVxcblxcbiAgKGRlZnRhYmxlIGFsbG9jYXRpb24tdGFibGU6e2FsbG9jYXRpb24tc2NoZW1hfSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50XFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBkYXRlOnRpbWVcXG4gICAgICBrZXlzZXQtcmVmOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgQGRvYyBcXFwiQWRkIGFuIGVudHJ5IHRvIHRoZSBjb2luIGFsbG9jYXRpb24gdGFibGUuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxhbHNvIGNyZWF0ZXMgYSBjb3JyZXNwb25kaW5nIGVtcHR5IGNvaW4gY29udHJhY3QgYWNjb3VudCBcXFxcXFxuICAgICAgICAgXFxcXG9mIHRoZSBzYW1lIG5hbWUgYW5kIGd1YXJkLiBSZXF1aXJlcyBHRU5FU0lTIGNhcGFiaWxpdHkuIFxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdFTkVTSVMpKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZSAoPj0gYW1vdW50IDAuMClcXG4gICAgICBcXFwiYWxsb2NhdGlvbiBhbW91bnQgbXVzdCBiZSBub24tbmVnYXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKGxldFxcbiAgICAgICgoZ3VhcmQ6Z3VhcmQgKGtleXNldC1yZWYtZ3VhcmQga2V5c2V0LXJlZikpKVxcblxcbiAgICAgIChjcmVhdGUtYWNjb3VudCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAgIChpbnNlcnQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IGFtb3VudFxcbiAgICAgICAgLCBcXFwiZGF0ZVxcXCIgOiBkYXRlXFxuICAgICAgICAsIFxcXCJndWFyZFxcXCIgOiBndWFyZFxcbiAgICAgICAgLCBcXFwicmVkZWVtZWRcXFwiIDogZmFsc2VcXG4gICAgICAgIH0pKSlcXG5cXG4gIChkZWZ1biByZWxlYXNlLWFsbG9jYXRpb25cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuXFxuICAgIEBkb2MgXFxcIlJlbGVhc2UgZnVuZHMgYXNzb2NpYXRlZCB3aXRoIGFsbG9jYXRpb24gQUNDT1VOVCBpbnRvIG1haW4gbGVkZ2VyLiAgIFxcXFxcXG4gICAgICAgICBcXFxcQUNDT1VOVCBtdXN0IGFscmVhZHkgZXhpc3QgaW4gbWFpbiBsZWRnZXIuIEFsbG9jYXRpb24gaXMgZGVhY3RpdmF0ZWQgXFxcXFxcbiAgICAgICAgIFxcXFxhZnRlciByZWxlYXNlLlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgICh3aXRoLXJlYWQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZVxcbiAgICAgICwgXFxcImRhdGVcXFwiIDo9IHJlbGVhc2UtdGltZVxcbiAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6PSByZWRlZW1lZFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBndWFyZFxcbiAgICAgIH1cXG5cXG4gICAgICAobGV0ICgoY3Vyci10aW1lOnRpbWUgKGF0ICdibG9jay10aW1lIChjaGFpbi1kYXRhKSkpKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKG5vdCByZWRlZW1lZClcXG4gICAgICAgICAgXFxcImFsbG9jYXRpb24gZnVuZHMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVkZWVtZWRcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2VcXG4gICAgICAgICAgKD49IGN1cnItdGltZSByZWxlYXNlLXRpbWUpXFxuICAgICAgICAgIChmb3JtYXQgXFxcImZ1bmRzIGxvY2tlZCB1bnRpbCB7fS4gY3VycmVudCB0aW1lOiB7fVxcXCIgW3JlbGVhc2UtdGltZSBjdXJyLXRpbWVdKSlcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKFJFTEVBU0VfQUxMT0NBVElPTiBhY2NvdW50IGJhbGFuY2UpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBiYWxhbmNlKSlcXG4gICAgICAgICAgKGNyZWRpdCBhY2NvdW50IGd1YXJkIGJhbGFuY2UpXFxuXFxuICAgICAgICAgICh1cGRhdGUgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgICAgeyBcXFwicmVkZWVtZWRcXFwiIDogdHJ1ZVxcbiAgICAgICAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgICAgICAgfSlcXG5cXG4gICAgICAgICAgXFxcIkFsbG9jYXRpb24gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIHRvIG1haW4gbGVkZ2VyXFxcIikpXFxuICAgICkpKVxcblxcbilcXG5cXG4oY3JlYXRlLXRhYmxlIGNvaW4tdGFibGUpXFxuKGNyZWF0ZS10YWJsZSBhbGxvY2F0aW9uLXRhYmxlKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiY29pbi1jb250cmFjdC12NS1pbnN0YWxsXCJ9In0" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IlRhYmxlQ3JlYXRlZCJ9LCJyZXFLZXkiOiI0cU4wUjM4d1R0OEN0bGVJaDU1djdZY3RPYjByc09uWjltd01ONlM0cFg4IiwibG9ncyI6IkwyaUNkQXlYNnptOUxPdXlzTlJ5T0JOS0ozbnlVb1NDR1VWamFyOU1ZZWsiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjozfQ" + , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" + , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" + , "- - eyJoYXNoIjoiWE5BNmxPSjFXLTdYWXY1WWI0QXFsNzRveFlJN09KcWpPUVQ1b1k0OHEtZyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wiYWxsb2NhdGlvbi10ZXN0MDFcIjpbXCI3MDExYzM3OTE0MGZmODk5ZjdlNjEzYWMwYTk3ODRhN2MwYTUxNWQzMGZlMGFiMmIwYzA3ZTVhYTE3NjM1ZTNlXCJdLFwiYWxsb2NhdGlvbjAyXCI6W1wiZTllNGU3MWJkMDYzZGNmN2UwNmJkNWIxYTE2Njg4ODk3ZDE1Y2E4YmQyZTUwOWM0NTNjNjE2MjE5YzE4NmNjNVwiXSxcImFsbG9jYXRpb24wMFwiOltcImQ4MmQwZGNkZTk4MjU1MDVkODZhZmI2ZGNjMTA0MTFkNmI2N2E0MjlhNzllMjFiZGE0YmIxMTliZjI4YWI4NzFcIl0sXCJhbGxvY2F0aW9uLXRlc3QwMlwiOltcIjA2NTQ0ZTIyYmZlZjIzMGQ2ZDIyZjk0ODZhYzZjYjc2YmYyNThlYmZiZjAxMzdlZTU4ZjY1NzQzZTFhNWI4YzRcIl0sXCJhbGxvY2F0aW9uMDFcIjpbXCJiNGM4YTNlYTkxZDMxNDZiMDU2MDk5NDc0MGYwZTNlZWQ5MWM1OWQyZWVjYTFkYzk5ZjBjMjg3Mjg0NWMyOTRkXCJdfSxcImNvZGVcIjpcIihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDBcXFwiIChyZWFkLWtleXNldCBcXFwiYWxsb2NhdGlvbjAwXFxcIikpXFxuKGRlZmluZS1rZXlzZXQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDFcXFwiKSlcXG4oZGVmaW5lLWtleXNldCBcXFwiYWxsb2NhdGlvbjAyXFxcIiAocmVhZC1rZXlzZXQgXFxcImFsbG9jYXRpb24wMlxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIpKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWtleXNldHNcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IlhOQTZsT0oxVy03WFl2NVliNEFxbDc0b3hZSTdPSnFqT1FUNW9ZNDhxLWciLCJsb2dzIjoiU0t3RUk1NWN4M2pMdU16RWRZQ1NtZ1RSRWhmVjg2alQwVjd3WVdvZWxfQSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjZ9" + , "- - eyJoYXNoIjoiNWtxZ0tzWWtwbjZCcS1KUnhNYnA1VG9jUWhRWGRJVVNiM1NGQm1Ba3VWcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMFxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMTVUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMFxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHRpbWUgXFxcIjIxMDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMVxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMlxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24tdGVzdDAxXFxcIiAodGltZSBcXFwiMTkwMC0xMC0zMVQxODowMDowMFpcXFwiKSBcXFwiYWxsb2NhdGlvbi10ZXN0MDFcXFwiIDEwMDAwMDAuMClcXG4oY29pbi5jcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24tdGVzdDAyXFxcIiAyMDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtYWxsb2NhdGlvbnNcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiI1a3FnS3NZa3BuNkJxLUpSeE1icDVUb2NRaFFYZElVU2IzU0ZCbUFrdVZzIiwibG9ncyI6IkRTeVFBWXBXSW9OazlOSUFNaTgzODRBTDhnSU9LRzc2Y1FaMzZNaGtkOGciLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo3fQ" + , "- - eyJoYXNoIjoiY2tiMmZnNWVJeXRoS3U5YVN3TkhIWllsWVY2V1R1NGlYaUZDamc5WFFLWSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwiZTdmN1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIl0sXCJzZW5kZXIwNVwiOltcImYwOWQ4ZjYzOTRhZWE0MjVmZTY3ODNkODhjZDgxMzYzZDgwMTdmMTZhZmQzNzExYzU3NWJlMGY1Y2Q1YzliYjlcIl0sXCJzZW5kZXIwNFwiOltcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl0sXCJtdWx0aS0wMi0wMy0wNC1hbnlcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcIjNhOWRkNTMyZDczZGFjZTE5NWRiYjY0ZDFkYmE2NTcyZmI3ODNkMGZkZDMyNDY4NWUzMmZiZGEyZjg5Zjk5YTZcIixcIjQzZjJhZGIxZGUxOTIwMDBjYjM3NzdiYWNjN2Y5ODNiNjYxNGZkOWMxNzE1Y2Q0NGNkNDg0YjZkM2EwZDM0YzhcIixcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl19LFwic2VuZGVyMDlcIjpbXCJjNTlkOTg0MGIwYjY2MDkwODM2NTQ2YjdlYjRhNzM2MDYyNTc1MjdlYzhjMmI0ODIzMDBmZDIyOTI2NGIwN2U2XCJdLFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXFxuXFxuKGNvaW4uY29pbmJhc2UgXFxcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcXFwiIChyZWFkLWtleXNldCBcXFwiZTdmN1xcXCIpIDE1MC4wKVxcblxcbjsgZW5vdWdoIHRvIGNvdmVyIHRoZSBnYXMgY29zdHMgZm9yIGFsbG9jYXRpb24gcmVsZWFzZVxcbihjb2luLmNvaW5iYXNlIFxcXCJhbGxvY2F0aW9uMDBcXFwiIChrZXlzZXQtcmVmLWd1YXJkIFxcXCJhbGxvY2F0aW9uMDBcXFwiKSAxMDAwMDAuMClcXG4oY29pbi5jb2luYmFzZSBcXFwiYWxsb2NhdGlvbjAxXFxcIiAoa2V5c2V0LXJlZi1ndWFyZCBcXFwiYWxsb2NhdGlvbjAxXFxcIikgMTAwMDAwLjApXFxuKGNvaW4uY29pbmJhc2UgXFxcImFsbG9jYXRpb24wMlxcXCIgKGtleXNldC1yZWYtZ3VhcmQgXFxcImFsbG9jYXRpb24wMlxcXCIpIDEwMDAwMC4wKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWdyYW50czBcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJja2IyZmc1ZUl5dGhLdTlhU3dOSEhaWWxZVjZXVHU0aVhpRkNqZzlYUUtZIiwibG9ncyI6ImdMVllyaXhlNHpPZWxpejNkYXlZTDhzWGd0RVVWczJQVndlWGRDYXBnVmsiLCJldmVudHMiOlt7InBhcmFtcyI6WyIiLCJzZW5kZXIwMCIsMTAwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMSIsMTEwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMiIsMTIwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMyIsMTMwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNCIsMTQwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNSIsMTUwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNiIsMTYwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNyIsMTcwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOCIsMTgwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOSIsMTkwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMC0wMSIsMTAxMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMi0wMy0wNC1hbnkiLDEyMzQwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiZTdmNzYzNGU5MjU1NDFmMzY4YjgyN2FkNWM3MjQyMTkwNTEwMGY2MjA1Mjg1YTc4YzE5ZDdiNGEzODcxMTgwNSIsMTUwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJhbGxvY2F0aW9uMDAiLDEwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiYWxsb2NhdGlvbjAxIiwxMDAwMDBdLCJuYW1lIjoiVFJBTlNGRVIiLCJtb2R1bGUiOnsibmFtZXNwYWNlIjpudWxsLCJuYW1lIjoiY29pbiJ9LCJtb2R1bGVIYXNoIjoiTTFnYWJha3FrRWlfMU44ZFJLdDR6NWxFdjFrdUNfbnhMVG55REN1WklLMCJ9LHsicGFyYW1zIjpbIiIsImFsbG9jYXRpb24wMiIsMTAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifV0sIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjh9" + , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" + , "transactionsHash: zeAGV98wWLDZvyuggueJs5kMnKo6WuRtHq7BIdzpmRk" + , "outputsHash: 8a5lydbrRwU8SqJN3Tx5T992hP9SWhLWL_WhIUqf5Do" + , "payloadHash: NMxVmmZPifTdTah43F733MN0Eb1i5xBEt6HUGAMNSm4" + , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" + , "" + ] diff --git a/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs b/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs new file mode 100644 index 0000000000..2e9a3563c2 --- /dev/null +++ b/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs @@ -0,0 +1,41 @@ +{-# LANGUAGE OverloadedStrings #-} + +-- This module is auto-generated. DO NOT EDIT IT MANUALLY. + +module Chainweb.BlockHeader.Genesis.FastDevelopment1to19Payload ( payloadBlock ) where + +import Data.Text.Encoding (encodeUtf8) +import qualified Data.Text as T +import Data.Yaml (decodeThrow) + +import Chainweb.Payload (PayloadWithOutputs) +import Chainweb.Utils (fromJuste) + +payloadBlock :: PayloadWithOutputs +payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines + [ "transactions:" + , "- - eyJoYXNoIjoiNDhUMExqQW5TRnBGV3h2dmFQVi1fNkUtQ2pEQVBoV1lVRldidnlmMmxGcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjFcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcbiAgIChkZWZ1biB0cmFuc2Zlci1jcmVhdGU6c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICApXFxuICAgICBAZG9jIFxcXCIgVHJhbnNmZXIgQU1PVU5UIGJldHdlZW4gYWNjb3VudHMgU0VOREVSIGFuZCBSRUNFSVZFUi4gXFxcXFxcbiAgICAgICAgICBcXFxcIEZhaWxzIGlmIFNFTkRFUiBkb2VzIG5vdCBleGlzdC4gSWYgUkVDRUlWRVIgZXhpc3RzLCBndWFyZCBcXFxcXFxuICAgICAgICAgIFxcXFwgbXVzdCBtYXRjaCBleGlzdGluZyB2YWx1ZS4gSWYgUkVDRUlWRVIgZG9lcyBub3QgZXhpc3QsIFxcXFxcXG4gICAgICAgICAgXFxcXCBSRUNFSVZFUiBhY2NvdW50IGlzIGNyZWF0ZWQgdXNpbmcgUkVDRUlWRVItR1VBUkQuIFxcXFxcXG4gICAgICAgICAgXFxcXCBTdWJqZWN0IHRvIG1hbmFnZW1lbnQgYnkgVFJBTlNGRVIgY2FwYWJpbGl0eS5cXFwiXFxuICAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHJlY2VpdmVyIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSlcXG4gICAgICAgICAgICBdXFxuICAgICApXFxuXFxuICAgKGRlZnBhY3QgdHJhbnNmZXItY3Jvc3NjaGFpbjpzdHJpbmdcXG4gICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIDItc3RlcCBwYWN0IHRvIHRyYW5zZmVyIEFNT1VOVCBmcm9tIFNFTkRFUiBvbiBjdXJyZW50IGNoYWluIFxcXFxcXG4gICAgICAgICAgXFxcXCB0byBSRUNFSVZFUiBvbiBUQVJHRVQtQ0hBSU4gdmlhIFNQViBwcm9vZi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFRBUkdFVC1DSEFJTiBtdXN0IGJlIGRpZmZlcmVudCB0aGFuIGN1cnJlbnQgY2hhaW4gaWQuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGaXJzdCBzdGVwIGRlYml0cyBBTU9VTlQgY29pbnMgaW4gU0VOREVSIGFjY291bnQgYW5kIHlpZWxkcyBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIsIFJFQ0VJVkVSX0dVQVJEIGFuZCBBTU9VTlQgdG8gVEFSR0VULUNIQUlOLiBcXFxcXFxuICAgICAgICAgIFxcXFwgU2Vjb25kIHN0ZXAgY29udGludWF0aW9uIGlzIHNlbnQgaW50byBUQVJHRVQtQ0hBSU4gd2l0aCBwcm9vZiBcXFxcXFxuICAgICAgICAgIFxcXFwgb2J0YWluZWQgZnJvbSB0aGUgc3B2ICdvdXRwdXQnIGVuZHBvaW50IG9mIENoYWlud2ViLiBcXFxcXFxuICAgICAgICAgIFxcXFwgUHJvb2YgaXMgdmFsaWRhdGVkIGFuZCBSRUNFSVZFUiBpcyBjcmVkaXRlZCB3aXRoIEFNT1VOVCBcXFxcXFxuICAgICAgICAgIFxcXFwgY3JlYXRpbmcgYWNjb3VudCB3aXRoIFJFQ0VJVkVSX0dVQVJEIGFzIG5lY2Vzc2FyeS5cXFwiXFxuICAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHJlY2VpdmVyIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gdGFyZ2V0LWNoYWluIFxcXCJcXFwiKSlcXG4gICAgICAgICAgICBdXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWxcXG4gICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAgXFxcIiBHZXQgYmFsYW5jZSBmb3IgQUNDT1VOVC4gRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGRldGFpbHM6b2JqZWN0e2FjY291bnQtZGV0YWlsc31cXG4gICAgICggYWNjb3VudDogc3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGFuIG9iamVjdCB3aXRoIGRldGFpbHMgb2YgQUNDT1VOVC4gXFxcXFxcbiAgICAgXFxcXCBGYWlscyBpZiBhY2NvdW50IGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gcHJlY2lzaW9uOmludGVnZXJcXG4gICAgICgpXFxuICAgICBcXFwiUmV0dXJuIHRoZSBtYXhpbXVtIGFsbG93ZWQgZGVjaW1hbCBwcmVjaXNpb24uXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbFxcbiAgICAgKCBhbW91bnQ6ZGVjaW1hbCApXFxuICAgICBcXFwiIEVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgdHJhbnNhY3Rpb25zLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nXFxuICAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgIGd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIENyZWF0ZSBBQ0NPVU5UIHdpdGggMC4wIGJhbGFuY2UsIHdpdGggR1VBUkQgY29udHJvbGxpbmcgYWNjZXNzLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gcm90YXRlOnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBuZXctZ3VhcmQ6Z3VhcmRcXG4gICAgIClcXG4gICAgIFxcXCIgUm90YXRlIGd1YXJkIGZvciBBQ0NPVU5ULiBUcmFuc2FjdGlvbiBpcyB2YWxpZGF0ZWQgYWdhaW5zdCBcXFxcXFxuICAgICBcXFxcIGV4aXN0aW5nIGd1YXJkIGJlZm9yZSBpbnN0YWxsaW5nIG5ldyBndWFyZC4gXFxcIlxcbiAgICAgKVxcblxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImdlbmVzaXMtMDFcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjEifSwicmVxS2V5IjoiNDhUMExqQW5TRnBGV3h2dmFQVi1fNkUtQ2pEQVBoV1lVRldidnlmMmxGcyIsImxvZ3MiOiJRRmEyOHRuOXkydFdMVzdUc3lHdzNOTkhpYzhxYjJ6UGtudXRpWWhnQXc4IiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MH0" + , "- - eyJoYXNoIjoieW5ucDFYVVNSTjJrMUYwYTZ2dXM3RFp0SDZjcHN6MVhmX0d3V0xnTFhTTSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUteGNoYWluLXYxXFxuXFxuICBcXFwiIFRoaXMgaW50ZXJmYWNlIG9mZmVycyBhIHN0YW5kYXJkIGNhcGFiaWxpdHkgZm9yIGNyb3NzLWNoYWluIFxcXFxcXG4gIFxcXFwgdHJhbnNmZXJzIGFuZCBhc3NvY2lhdGVkIGV2ZW50cy4gXFxcIlxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU46Ym9vbFxcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgKVxcbiAgICBAZG9jIFxcXCIgTWFuYWdlZCBjYXBhYmlsaXR5IHNlYWxpbmcgQU1PVU5UIGZvciB0cmFuc2ZlciBcXFxcXFxuICAgICAgICAgXFxcXCBmcm9tIFNFTkRFUiB0byBSRUNFSVZFUiBvbiBUQVJHRVQtQ0hBSU4uIFBlcm1pdHMgXFxcXFxcbiAgICAgICAgIFxcXFwgYW55IG51bWJlciBvZiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdXAgdG8gQU1PVU5ULlxcXCJcXG5cXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSX1hDSEFJTi1tZ3JcXG4gICAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG4gICAgQGRvYyBcXFwiIEFsbG93cyBUUkFOU0ZFUi1YQ0hBSU4gQU1PVU5UIHRvIGJlIGxlc3MgdGhhbiBvciBcXFxcXFxuICAgICAgICAgXFxcXCBlcXVhbCBtYW5hZ2VkIHF1YW50aXR5IGFzIGEgb25lLXNob3QsIHJldHVybmluZyAwLjAuXFxcIlxcbiAgKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUl9YQ0hBSU5fUkVDRDpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgc291cmNlLWNoYWluOnN0cmluZ1xcbiAgICApXFxuICAgIEBkb2MgXFxcIkV2ZW50IGVtaXR0ZWQgb24gcmVjZWlwdCBvZiBjcm9zcy1jaGFpbiB0cmFuc2Zlci5cXFwiXFxuICAgIEBldmVudFxcbiAgKVxcbilcXG5cIn19LFwic2lnbmVyc1wiOltdLFwibWV0YVwiOntcImNyZWF0aW9uVGltZVwiOjAsXCJ0dGxcIjoxNzI4MDAsXCJnYXNMaW1pdFwiOjAsXCJjaGFpbklkXCI6XCJcIixcImdhc1ByaWNlXCI6MCxcInNlbmRlclwiOlwiXCJ9LFwibm9uY2VcIjpcImdlbmVzaXMteGNoYWluXCJ9In0" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUteGNoYWluLXYxIn0sInJlcUtleSI6InlubnAxWFVTUk4yazFGMGE2dnVzN0RadEg2Y3BzejFYZl9Hd1dMZ0xYU00iLCJsb2dzIjoiVmRxMHp0SjBnRThOLTNkLW1tRmRRXzZ0ZGp0dnEtNXIwX050dXBvT1hpVSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjF9" + , "- - eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjIifSwicmVxS2V5IjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsImxvZ3MiOiJWaGlrLVYzOHByQXRpbHhTV3RZNWYxUnpmVjFUYVJBQzF0N3VVVXZjbGxnIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6Mn0" + , "- - eyJoYXNoIjoiNHFOMFIzOHdUdDhDdGxlSWg1NXY3WWN0T2IwcnNPblo5bXdNTjZTNHBYOCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIlxcbihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG4gIChpbXBsZW1lbnRzIGZ1bmdpYmxlLXhjaGFpbi12MSlcXG5cXG4gIDs7IGNvaW4tdjJcXG4gIChibGVzcyBcXFwidXRfSl9aTmtveWFQVUVKaGl3VmVXbmtTUW45SlQ5c1FDV0tkampWVnJXb1xcXCIpXFxuXFxuICA7OyBjb2luIHYzXFxuICAoYmxlc3MgXFxcIjFvc19zTEFVWXZCenNwbjVqamF3dFJwSldpSDFXUGZoeU5yYWVWdlNJd1VcXFwiKVxcblxcbiAgOzsgY29pbiB2NFxcbiAgKGJsZXNzIFxcXCJCalpXMFQyYWM2cUVfSTVYOEdFNGZhbDZ0VHFqaExUQzdteTB5dFFTeExVXFxcIilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICApXFxuXFxuICAgIEBtYW5hZ2VkIGFtb3VudCBUUkFOU0ZFUl9YQ0hBSU4tbWdyXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiQ3Jvc3MtY2hhaW4gdHJhbnNmZXJzIHJlcXVpcmUgYSBwb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSX1hDSEFJTi1tZ3I6ZGVjaW1hbFxcbiAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgIHJlcXVlc3RlZDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgKGVuZm9yY2UgKD49IG1hbmFnZWQgcmVxdWVzdGVkKVxcbiAgICAgIChmb3JtYXQgXFxcIlRSQU5TRkVSX1hDSEFJTiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgMC4wXFxuICApXFxuXFxuICAoZGVmY2FwIFRSQU5TRkVSX1hDSEFJTl9SRUNEOmJvb2xcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgICBzb3VyY2UtY2hhaW46c3RyaW5nXFxuICAgIClcXG4gICAgQGV2ZW50IHRydWVcXG4gIClcXG5cXG4gIDsgdjMgY2FwYWJpbGl0aWVzXFxuICAoZGVmY2FwIFJFTEVBU0VfQUxMT0NBVElPTlxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgKVxcbiAgICBAZG9jIFxcXCJFdmVudCBmb3IgYWxsb2NhdGlvbiByZWxlYXNlLCBjYW4gYmUgdXNlZCBmb3Igc2lnIHNjb3BpbmcuXFxcIlxcbiAgICBAZXZlbnQgdHJ1ZVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb25zdGFudHNcXG5cXG4gIChkZWZjb25zdCBDT0lOX0NIQVJTRVQgQ0hBUlNFVF9MQVRJTjFcXG4gICAgXFxcIlRoZSBkZWZhdWx0IGNvaW4gY29udHJhY3QgY2hhcmFjdGVyIHNldFxcXCIpXFxuXFxuICAoZGVmY29uc3QgTUlOSU1VTV9QUkVDSVNJT04gMTJcXG4gICAgXFxcIk1pbmltdW0gYWxsb3dlZCBwcmVjaXNpb24gZm9yIGNvaW4gdHJhbnNhY3Rpb25zXFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIIDNcXG4gICAgXFxcIk1pbmltdW0gYWNjb3VudCBsZW5ndGggYWRtaXNzaWJsZSBmb3IgY29pbiBhY2NvdW50c1xcXCIpXFxuXFxuICAoZGVmY29uc3QgTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSCAyNTZcXG4gICAgXFxcIk1heGltdW0gYWNjb3VudCBuYW1lIGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBWQUxJRF9DSEFJTl9JRFMgKG1hcCAoaW50LXRvLXN0ciAxMCkgKGVudW1lcmF0ZSAwIDE5KSlcXG4gICAgXFxcIkxpc3Qgb2YgYWxsIHZhbGlkIENoYWlud2ViIGNoYWluIGlkc1xcXCIpXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IFV0aWxpdGllc1xcblxcbiAgKGRlZnVuIGVuZm9yY2UtdW5pdDpib29sIChhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSBtaW5pbXVtIHByZWNpc2lvbiBhbGxvd2VkIGZvciBjb2luIHRyYW5zYWN0aW9uc1xcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoPSAoZmxvb3IgYW1vdW50IE1JTklNVU1fUFJFQ0lTSU9OKVxcbiAgICAgICAgIGFtb3VudClcXG4gICAgICAoZm9ybWF0IFxcXCJBbW91bnQgdmlvbGF0ZXMgbWluaW11bSBwcmVjaXNpb246IHt9XFxcIiBbYW1vdW50XSkpXFxuICAgIClcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZS1hY2NvdW50IChhY2NvdW50OnN0cmluZylcXG4gICAgQGRvYyBcXFwiRW5mb3JjZSB0aGF0IGFuIGFjY291bnQgbmFtZSBjb25mb3JtcyB0byB0aGUgY29pbiBjb250cmFjdCBcXFxcXFxuICAgICAgICAgXFxcXG1pbmltdW0gYW5kIG1heGltdW0gbGVuZ3RoIHJlcXVpcmVtZW50cywgYXMgd2VsbCBhcyB0aGUgICAgXFxcXFxcbiAgICAgICAgIFxcXFxsYXRpbi0xIGNoYXJhY3RlciBzZXQuXFxcIlxcblxcbiAgICAoZW5mb3JjZVxcbiAgICAgIChpcy1jaGFyc2V0IENPSU5fQ0hBUlNFVCBhY2NvdW50KVxcbiAgICAgIChmb3JtYXRcXG4gICAgICAgIFxcXCJBY2NvdW50IGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIGNvaW4gY29udHJhY3QgY2hhcnNldDoge31cXFwiXFxuICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAobGV0ICgoYWNjb3VudC1sZW5ndGggKGxlbmd0aCBhY2NvdW50KSkpXFxuXFxuICAgICAgKGVuZm9yY2VcXG4gICAgICAgICg-PSBhY2NvdW50LWxlbmd0aCBNSU5JTVVNX0FDQ09VTlRfTEVOR1RIKVxcbiAgICAgICAgKGZvcm1hdFxcbiAgICAgICAgICBcXFwiQWNjb3VudCBuYW1lIGRvZXMgbm90IGNvbmZvcm0gdG8gdGhlIG1pbiBsZW5ndGggcmVxdWlyZW1lbnQ6IHt9XFxcIlxcbiAgICAgICAgICBbYWNjb3VudF0pKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPD0gYWNjb3VudC1sZW5ndGggTUFYSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtYXggbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG4gICAgICApXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gQ29udHJhY3RcXG5cXG4gIChkZWZ1biBnYXMtb25seSAoKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMtb25seSB1c2VyIGd1YXJkcy5cXFwiXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpKVxcblxcbiAgKGRlZnVuIGdhcy1ndWFyZCAoZ3VhcmQ6Z3VhcmQpXFxuICAgIFxcXCJQcmVkaWNhdGUgZm9yIGdhcyArIHNpbmdsZSBrZXkgdXNlciBndWFyZHNcXFwiXFxuICAgIChlbmZvcmNlLW9uZVxcbiAgICAgIFxcXCJFbmZvcmNlIGVpdGhlciB0aGUgcHJlc2VuY2Ugb2YgYSBHQVMgY2FwIG9yIGtleXNldFxcXCJcXG4gICAgICBbIChnYXMtb25seSlcXG4gICAgICAgIChlbmZvcmNlLWd1YXJkIGd1YXJkKVxcbiAgICAgIF0pKVxcblxcbiAgKGRlZnVuIGJ1eS1nYXM6c3RyaW5nIChzZW5kZXI6c3RyaW5nIHRvdGFsOmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIlRoaXMgZnVuY3Rpb24gZGVzY3JpYmVzIHRoZSBtYWluICdnYXMgYnV5JyBvcGVyYXRpb24uIEF0IHRoaXMgcG9pbnQgXFxcXFxcbiAgICBcXFxcTUlORVIgaGFzIGJlZW4gY2hvc2VuIGZyb20gdGhlIHBvb2wsIGFuZCB3aWxsIGJlIHZhbGlkYXRlZC4gVGhlIFNFTkRFUiAgIFxcXFxcXG4gICAgXFxcXG9mIHRoaXMgdHJhbnNhY3Rpb24gaGFzIHNwZWNpZmllZCBhIGdhcyBsaW1pdCBMSU1JVCAobWF4aW11bSBnYXMpIGZvciAgICBcXFxcXFxuICAgIFxcXFx0aGUgdHJhbnNhY3Rpb24sIGFuZCB0aGUgcHJpY2UgaXMgdGhlIHNwb3QgcHJpY2Ugb2YgZ2FzIGF0IHRoYXQgdGltZS4gICAgXFxcXFxcbiAgICBcXFxcVGhlIGdhcyBidXkgd2lsbCBiZSBleGVjdXRlZCBwcmlvciB0byBleGVjdXRpbmcgU0VOREVSJ3MgY29kZS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcblxcbiAgICAoZW5mb3JjZS11bml0IHRvdGFsKVxcbiAgICAoZW5mb3JjZSAoPiB0b3RhbCAwLjApIFxcXCJnYXMgc3VwcGx5IG11c3QgYmUgYSBwb3NpdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKERFQklUIHNlbmRlcilcXG4gICAgICAoZGViaXQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlZGVlbS1nYXM6c3RyaW5nIChtaW5lcjpzdHJpbmcgbWluZXItZ3VhcmQ6Z3VhcmQgc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAncmVkZWVtIGdhcycgb3BlcmF0aW9uLiBBdCB0aGlzICAgIFxcXFxcXG4gICAgXFxcXHBvaW50LCB0aGUgU0VOREVSJ3MgdHJhbnNhY3Rpb24gaGFzIGJlZW4gZXhlY3V0ZWQsIGFuZCB0aGUgZ2FzIHRoYXQgICAgICBcXFxcXFxuICAgIFxcXFx3YXMgY2hhcmdlZCBoYXMgYmVlbiBjYWxjdWxhdGVkLiBNSU5FUiB3aWxsIGJlIGNyZWRpdGVkIHRoZSBnYXMgY29zdCwgICAgXFxcXFxcbiAgICBcXFxcYW5kIFNFTkRFUiB3aWxsIHJlY2VpdmUgdGhlIHJlbWFpbmRlciB1cCB0byB0aGUgbGltaXRcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBtaW5lcilcXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoR0FTKSlcXG4gICAgKGxldCpcXG4gICAgICAoKGZlZSAocmVhZC1kZWNpbWFsIFxcXCJmZWVcXFwiKSlcXG4gICAgICAgKHJlZnVuZCAoLSB0b3RhbCBmZWUpKSlcXG5cXG4gICAgICAoZW5mb3JjZS11bml0IGZlZSlcXG4gICAgICAoZW5mb3JjZSAoPj0gZmVlIDAuMClcXG4gICAgICAgIFxcXCJmZWUgbXVzdCBiZSBhIG5vbi1uZWdhdGl2ZSBxdWFudGl0eVxcXCIpXFxuXFxuICAgICAgKGVuZm9yY2UgKD49IHJlZnVuZCAwLjApXFxuICAgICAgICBcXFwicmVmdW5kIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgbWluZXIgZmVlKSkgO3YzXFxuXFxuICAgICAgICA7IGRpcmVjdGx5IHVwZGF0ZSBpbnN0ZWFkIG9mIGNyZWRpdFxcbiAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBzZW5kZXIpXFxuICAgICAgICAoaWYgKD4gcmVmdW5kIDAuMClcXG4gICAgICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgICAgICAgICh1cGRhdGUgY29pbi10YWJsZSBzZW5kZXJcXG4gICAgICAgICAgICAgIHsgXFxcImJhbGFuY2VcXFwiOiAoKyBiYWxhbmNlIHJlZnVuZCkgfSkpXFxuXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuXFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIG1pbmVyKVxcbiAgICAgICAgKGlmICg-IGZlZSAwLjApXFxuICAgICAgICAgIChjcmVkaXQgbWluZXIgbWluZXItZ3VhcmQgZmVlKVxcbiAgICAgICAgICBcXFwibm9vcFxcXCIpKVxcbiAgICAgIClcXG5cXG4gICAgKVxcblxcbiAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgZ3VhcmQ6Z3VhcmQpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS1yZXNlcnZlZCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAoaW5zZXJ0IGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiBndWFyZFxcbiAgICAgIH0pXFxuICAgIClcXG5cXG4gIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsIChhY2NvdW50OnN0cmluZylcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG4gICAgICBiYWxhbmNlXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gZGV0YWlsczpvYmplY3R7ZnVuZ2libGUtdjIuYWNjb3VudC1kZXRhaWxzfVxcbiAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZyB9XFxuICAgICAgeyBcXFwiYWNjb3VudFxcXCIgOiBhY2NvdW50XFxuICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiBiYWxcXG4gICAgICAsIFxcXCJndWFyZFxcXCI6IGcgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJvdGF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIG5ldy1ndWFyZDpndWFyZClcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoUk9UQVRFIGFjY291bnQpXFxuICAgICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBvbGQtZ3VhcmQgfVxcblxcbiAgICAgICAgKGVuZm9yY2UtZ3VhcmQgb2xkLWd1YXJkKVxcblxcbiAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDogbmV3LWd1YXJkIH1cXG4gICAgICAgICAgKSkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBwcmVjaXNpb246aW50ZWdlclxcbiAgICAoKVxcbiAgICBNSU5JTVVNX1BSRUNJU0lPTilcXG5cXG4gIChkZWZ1biB0cmFuc2ZlcjpzdHJpbmcgKHNlbmRlcjpzdHJpbmcgcmVjZWl2ZXI6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgY29uc2VydmVzLW1hc3MpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCByZWNlaXZlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gc2VuZGVyIHJlY2VpdmVyKSkgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgcmVjZWl2ZXJcXG4gICAgICAgIHsgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG5cXG4gICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgZyBhbW91bnQpKVxcbiAgICAgIClcXG4gICAgKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyLWNyZWF0ZTpzdHJpbmdcXG4gICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgICBhbW91bnQ6ZGVjaW1hbCApXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgXVxcblxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKVxcbiAgICAgIFxcXCJzZW5kZXIgY2Fubm90IGJlIHRoZSByZWNlaXZlciBvZiBhIHRyYW5zZmVyXFxcIilcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwidHJhbnNmZXIgYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoVFJBTlNGRVIgc2VuZGVyIHJlY2VpdmVyIGFtb3VudClcXG4gICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biBjb2luYmFzZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFjY291bnQtZ3VhcmQ6Z3VhcmQgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkludGVybmFsIGZ1bmN0aW9uIGZvciB0aGUgaW5pdGlhbCBjcmVhdGlvbiBvZiBjb2lucy4gIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICBcXFxcY2Fubm90IGJlIHVzZWQgb3V0c2lkZSBvZiB0aGUgY29pbiBjb250cmFjdC5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoQ09JTkJBU0UpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIGFjY291bnQpXFxuICAgICAgKGNyZWRpdCBhY2NvdW50IGFjY291bnQtZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIHJlbWVkaWF0ZTpzdHJpbmcgKGFjY291bnQ6c3RyaW5nIGFtb3VudDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJBbGxvd3MgZm9yIHJlbWVkaWF0aW9uIHRyYW5zYWN0aW9ucy4gVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgICAgICAgXFxcXGlzIHByb3RlY3RlZCBieSB0aGUgUkVNRURJQVRFIGNhcGFiaWxpdHlcXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJSZW1lZGlhdGlvbiBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChSRU1FRElBVEUpKVxcbiAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBhbW91bnQpKSA7djNcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogLTEuMCwgXFxcImd1YXJkXFxcIiA6IGd1YXJkIH1cXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlLCBcXFwiZ3VhcmRcXFwiIDo9IHJldGcgfVxcbiAgICAgIDsgd2UgZG9uJ3Qgd2FudCB0byBvdmVyd3JpdGUgYW4gZXhpc3RpbmcgZ3VhcmQgd2l0aCB0aGUgdXNlci1zdXBwbGllZCBvbmVcXG4gICAgICAoZW5mb3JjZSAoPSByZXRnIGd1YXJkKVxcbiAgICAgICAgXFxcImFjY291bnQgZ3VhcmRzIGRvIG5vdCBtYXRjaFxcXCIpXFxuXFxuICAgICAgKGxldCAoKGlzLW5ld1xcbiAgICAgICAgICAgICAoaWYgKD0gYmFsYW5jZSAtMS4wKVxcbiAgICAgICAgICAgICAgICAgKGVuZm9yY2UtcmVzZXJ2ZWQgYWNjb3VudCBndWFyZClcXG4gICAgICAgICAgICAgICBmYWxzZSkpKVxcblxcbiAgICAgICAgKHdyaXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IChpZiBpcy1uZXcgYW1vdW50ICgrIGJhbGFuY2UgYW1vdW50KSlcXG4gICAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICAgIH0pKVxcbiAgICAgICkpXFxuXFxuICAoZGVmdW4gY2hlY2stcmVzZXJ2ZWQ6c3RyaW5nIChhY2NvdW50OnN0cmluZylcXG4gICAgXFxcIiBDaGVja3MgQUNDT1VOVCBmb3IgcmVzZXJ2ZWQgbmFtZSBhbmQgcmV0dXJucyB0eXBlIGlmIFxcXFxcXG4gICAgXFxcXCBmb3VuZCBvciBlbXB0eSBzdHJpbmcuIFJlc2VydmVkIG5hbWVzIHN0YXJ0IHdpdGggYSBcXFxcXFxuICAgIFxcXFwgc2luZ2xlIGNoYXIgYW5kIGNvbG9uLCBlLmcuICdjOmZvbycsIHdoaWNoIHdvdWxkIHJldHVybiAnYycgYXMgdHlwZS5cXFwiXFxuICAgIChsZXQgKChwZnggKHRha2UgMiBhY2NvdW50KSkpXFxuICAgICAgKGlmICg9IFxcXCI6XFxcIiAodGFrZSAtMSBwZngpKSAodGFrZSAxIHBmeCkgXFxcIlxcXCIpKSlcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXJlc2VydmVkOmJvb2wgKGFjY291bnQ6c3RyaW5nIGd1YXJkOmd1YXJkKVxcbiAgICBAZG9jIFxcXCJFbmZvcmNlIHJlc2VydmVkIGFjY291bnQgbmFtZSBwcm90b2NvbHMuXFxcIlxcbiAgICAoaWYgKHZhbGlkYXRlLXByaW5jaXBhbCBndWFyZCBhY2NvdW50KVxcbiAgICAgIHRydWVcXG4gICAgICAobGV0ICgociAoY2hlY2stcmVzZXJ2ZWQgYWNjb3VudCkpKVxcbiAgICAgICAgKGlmICg9IHIgXFxcIlxcXCIpXFxuICAgICAgICAgIHRydWVcXG4gICAgICAgICAgKGlmICg9IHIgXFxcImtcXFwiKVxcbiAgICAgICAgICAgIChlbmZvcmNlIGZhbHNlIFxcXCJTaW5nbGUta2V5IGFjY291bnQgcHJvdG9jb2wgdmlvbGF0aW9uXFxcIilcXG4gICAgICAgICAgICAoZW5mb3JjZSBmYWxzZVxcbiAgICAgICAgICAgICAgKGZvcm1hdCBcXFwiUmVzZXJ2ZWQgcHJvdG9jb2wgZ3VhcmQgdmlvbGF0aW9uOiB7fVxcXCIgW3JdKSlcXG4gICAgICAgICAgICApKSkpKVxcblxcblxcbiAgKGRlZnNjaGVtYSBjcm9zc2NoYWluLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHlpZWxkZWQgdmFsdWUgaW4gY3Jvc3MtY2hhaW4gdHJhbnNmZXJzXFxcIlxcbiAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgcmVjZWl2ZXItZ3VhcmQ6Z3VhcmRcXG4gICAgYW1vdW50OmRlY2ltYWxcXG4gICAgc291cmNlLWNoYWluOnN0cmluZylcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5XFxuICAgICAgICAoVFJBTlNGRVJfWENIQUlOIHNlbmRlciByZWNlaXZlciBhbW91bnQgdGFyZ2V0LWNoYWluKVxcblxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgc2VuZGVyKVxcbiAgICAgICAgKHZhbGlkYXRlLWFjY291bnQgcmVjZWl2ZXIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoIT0gXFxcIlxcXCIgdGFyZ2V0LWNoYWluKSBcXFwiZW1wdHkgdGFyZ2V0LWNoYWluXFxcIilcXG4gICAgICAgIChlbmZvcmNlICghPSAoYXQgJ2NoYWluLWlkIChjaGFpbi1kYXRhKSkgdGFyZ2V0LWNoYWluKVxcbiAgICAgICAgICBcXFwiY2Fubm90IHJ1biBjcm9zcy1jaGFpbiB0cmFuc2ZlcnMgdG8gdGhlIHNhbWUgY2hhaW5cXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICAgICAgXFxcInRyYW5zZmVyIHF1YW50aXR5IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoY29udGFpbnMgdGFyZ2V0LWNoYWluIFZBTElEX0NIQUlOX0lEUylcXG4gICAgICAgICAgXFxcInRhcmdldCBjaGFpbiBpcyBub3QgYSB2YWxpZCBjaGFpbndlYiBjaGFpbiBpZFxcXCIpXFxuXFxuICAgICAgICA7OyBzdGVwIDEgLSBkZWJpdCBkZWxldGUtYWNjb3VudCBvbiBjdXJyZW50IGNoYWluXFxuICAgICAgICAoZGViaXQgc2VuZGVyIGFtb3VudClcXG4gICAgICAgIChlbWl0LWV2ZW50IChUUkFOU0ZFUiBzZW5kZXIgXFxcIlxcXCIgYW1vdW50KSlcXG5cXG4gICAgICAgIChsZXRcXG4gICAgICAgICAgKChjcm9zc2NoYWluLWRldGFpbHM6b2JqZWN0e2Nyb3NzY2hhaW4tc2NoZW1hfVxcbiAgICAgICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6IHJlY2VpdmVyXFxuICAgICAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDogcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDogYW1vdW50XFxuICAgICAgICAgICAgLCBcXFwic291cmNlLWNoYWluXFxcIiA6IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKVxcbiAgICAgICAgICAgIH0pKVxcbiAgICAgICAgICAoeWllbGQgY3Jvc3NjaGFpbi1kZXRhaWxzIHRhcmdldC1jaGFpbilcXG4gICAgICAgICAgKSkpXFxuXFxuICAgIChzdGVwXFxuICAgICAgKHJlc3VtZVxcbiAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDo9IHJlY2VpdmVyXFxuICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOj0gcmVjZWl2ZXItZ3VhcmRcXG4gICAgICAgICwgXFxcImFtb3VudFxcXCIgOj0gYW1vdW50XFxuICAgICAgICAsIFxcXCJzb3VyY2UtY2hhaW5cXFwiIDo9IHNvdXJjZS1jaGFpblxcbiAgICAgICAgfVxcblxcbiAgICAgICAgKGVtaXQtZXZlbnQgKFRSQU5TRkVSIFxcXCJcXFwiIHJlY2VpdmVyIGFtb3VudCkpXFxuICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVJfWENIQUlOX1JFQ0QgXFxcIlxcXCIgcmVjZWl2ZXIgYW1vdW50IHNvdXJjZS1jaGFpbikpXFxuXFxuICAgICAgICA7OyBzdGVwIDIgLSBjcmVkaXQgY3JlYXRlIGFjY291bnQgb24gdGFyZ2V0IGNoYWluXFxuICAgICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpXFxuICAgICAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgQ29pbiBhbGxvY2F0aW9uc1xcblxcbiAgKGRlZnNjaGVtYSBhbGxvY2F0aW9uLXNjaGVtYVxcbiAgICBAZG9jIFxcXCJHZW5lc2lzIGFsbG9jYXRpb24gcmVnaXN0cnlcXFwiXFxuICAgIDtAbW9kZWwgWyAoaW52YXJpYW50ICg-PSBiYWxhbmNlIDAuMCkpIF1cXG5cXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGRhdGU6dGltZVxcbiAgICBndWFyZDpndWFyZFxcbiAgICByZWRlZW1lZDpib29sKVxcblxcbiAgKGRlZnRhYmxlIGFsbG9jYXRpb24tdGFibGU6e2FsbG9jYXRpb24tc2NoZW1hfSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50XFxuICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICBkYXRlOnRpbWVcXG4gICAgICBrZXlzZXQtcmVmOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG5cXG4gICAgQGRvYyBcXFwiQWRkIGFuIGVudHJ5IHRvIHRoZSBjb2luIGFsbG9jYXRpb24gdGFibGUuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxhbHNvIGNyZWF0ZXMgYSBjb3JyZXNwb25kaW5nIGVtcHR5IGNvaW4gY29udHJhY3QgYWNjb3VudCBcXFxcXFxuICAgICAgICAgXFxcXG9mIHRoZSBzYW1lIG5hbWUgYW5kIGd1YXJkLiBSZXF1aXJlcyBHRU5FU0lTIGNhcGFiaWxpdHkuIFxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdFTkVTSVMpKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcbiAgICAoZW5mb3JjZSAoPj0gYW1vdW50IDAuMClcXG4gICAgICBcXFwiYWxsb2NhdGlvbiBhbW91bnQgbXVzdCBiZSBub24tbmVnYXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKGxldFxcbiAgICAgICgoZ3VhcmQ6Z3VhcmQgKGtleXNldC1yZWYtZ3VhcmQga2V5c2V0LXJlZikpKVxcblxcbiAgICAgIChjcmVhdGUtYWNjb3VudCBhY2NvdW50IGd1YXJkKVxcblxcbiAgICAgIChpbnNlcnQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6IGFtb3VudFxcbiAgICAgICAgLCBcXFwiZGF0ZVxcXCIgOiBkYXRlXFxuICAgICAgICAsIFxcXCJndWFyZFxcXCIgOiBndWFyZFxcbiAgICAgICAgLCBcXFwicmVkZWVtZWRcXFwiIDogZmFsc2VcXG4gICAgICAgIH0pKSlcXG5cXG4gIChkZWZ1biByZWxlYXNlLWFsbG9jYXRpb25cXG4gICAgKCBhY2NvdW50OnN0cmluZyApXFxuXFxuICAgIEBkb2MgXFxcIlJlbGVhc2UgZnVuZHMgYXNzb2NpYXRlZCB3aXRoIGFsbG9jYXRpb24gQUNDT1VOVCBpbnRvIG1haW4gbGVkZ2VyLiAgIFxcXFxcXG4gICAgICAgICBcXFxcQUNDT1VOVCBtdXN0IGFscmVhZHkgZXhpc3QgaW4gbWFpbiBsZWRnZXIuIEFsbG9jYXRpb24gaXMgZGVhY3RpdmF0ZWQgXFxcXFxcbiAgICAgICAgIFxcXFxhZnRlciByZWxlYXNlLlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgICh3aXRoLXJlYWQgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZVxcbiAgICAgICwgXFxcImRhdGVcXFwiIDo9IHJlbGVhc2UtdGltZVxcbiAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6PSByZWRlZW1lZFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBndWFyZFxcbiAgICAgIH1cXG5cXG4gICAgICAobGV0ICgoY3Vyci10aW1lOnRpbWUgKGF0ICdibG9jay10aW1lIChjaGFpbi1kYXRhKSkpKVxcblxcbiAgICAgICAgKGVuZm9yY2UgKG5vdCByZWRlZW1lZClcXG4gICAgICAgICAgXFxcImFsbG9jYXRpb24gZnVuZHMgaGF2ZSBhbHJlYWR5IGJlZW4gcmVkZWVtZWRcXFwiKVxcblxcbiAgICAgICAgKGVuZm9yY2VcXG4gICAgICAgICAgKD49IGN1cnItdGltZSByZWxlYXNlLXRpbWUpXFxuICAgICAgICAgIChmb3JtYXQgXFxcImZ1bmRzIGxvY2tlZCB1bnRpbCB7fS4gY3VycmVudCB0aW1lOiB7fVxcXCIgW3JlbGVhc2UtdGltZSBjdXJyLXRpbWVdKSlcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKFJFTEVBU0VfQUxMT0NBVElPTiBhY2NvdW50IGJhbGFuY2UpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoZW1pdC1ldmVudCAoVFJBTlNGRVIgXFxcIlxcXCIgYWNjb3VudCBiYWxhbmNlKSlcXG4gICAgICAgICAgKGNyZWRpdCBhY2NvdW50IGd1YXJkIGJhbGFuY2UpXFxuXFxuICAgICAgICAgICh1cGRhdGUgYWxsb2NhdGlvbi10YWJsZSBhY2NvdW50XFxuICAgICAgICAgICAgeyBcXFwicmVkZWVtZWRcXFwiIDogdHJ1ZVxcbiAgICAgICAgICAgICwgXFxcImJhbGFuY2VcXFwiIDogMC4wXFxuICAgICAgICAgICAgfSlcXG5cXG4gICAgICAgICAgXFxcIkFsbG9jYXRpb24gc3VjY2Vzc2Z1bGx5IHJlbGVhc2VkIHRvIG1haW4gbGVkZ2VyXFxcIikpXFxuICAgICkpKVxcblxcbilcXG5cXG4oY3JlYXRlLXRhYmxlIGNvaW4tdGFibGUpXFxuKGNyZWF0ZS10YWJsZSBhbGxvY2F0aW9uLXRhYmxlKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiY29pbi1jb250cmFjdC12NS1pbnN0YWxsXCJ9In0" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IlRhYmxlQ3JlYXRlZCJ9LCJyZXFLZXkiOiI0cU4wUjM4d1R0OEN0bGVJaDU1djdZY3RPYjByc09uWjltd01ONlM0cFg4IiwibG9ncyI6IkwyaUNkQXlYNnptOUxPdXlzTlJ5T0JOS0ozbnlVb1NDR1VWamFyOU1ZZWsiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjozfQ" + , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" + , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" + , "- - eyJoYXNoIjoiWE5BNmxPSjFXLTdYWXY1WWI0QXFsNzRveFlJN09KcWpPUVQ1b1k0OHEtZyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wiYWxsb2NhdGlvbi10ZXN0MDFcIjpbXCI3MDExYzM3OTE0MGZmODk5ZjdlNjEzYWMwYTk3ODRhN2MwYTUxNWQzMGZlMGFiMmIwYzA3ZTVhYTE3NjM1ZTNlXCJdLFwiYWxsb2NhdGlvbjAyXCI6W1wiZTllNGU3MWJkMDYzZGNmN2UwNmJkNWIxYTE2Njg4ODk3ZDE1Y2E4YmQyZTUwOWM0NTNjNjE2MjE5YzE4NmNjNVwiXSxcImFsbG9jYXRpb24wMFwiOltcImQ4MmQwZGNkZTk4MjU1MDVkODZhZmI2ZGNjMTA0MTFkNmI2N2E0MjlhNzllMjFiZGE0YmIxMTliZjI4YWI4NzFcIl0sXCJhbGxvY2F0aW9uLXRlc3QwMlwiOltcIjA2NTQ0ZTIyYmZlZjIzMGQ2ZDIyZjk0ODZhYzZjYjc2YmYyNThlYmZiZjAxMzdlZTU4ZjY1NzQzZTFhNWI4YzRcIl0sXCJhbGxvY2F0aW9uMDFcIjpbXCJiNGM4YTNlYTkxZDMxNDZiMDU2MDk5NDc0MGYwZTNlZWQ5MWM1OWQyZWVjYTFkYzk5ZjBjMjg3Mjg0NWMyOTRkXCJdfSxcImNvZGVcIjpcIihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDBcXFwiIChyZWFkLWtleXNldCBcXFwiYWxsb2NhdGlvbjAwXFxcIikpXFxuKGRlZmluZS1rZXlzZXQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDFcXFwiKSlcXG4oZGVmaW5lLWtleXNldCBcXFwiYWxsb2NhdGlvbjAyXFxcIiAocmVhZC1rZXlzZXQgXFxcImFsbG9jYXRpb24wMlxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIpKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWtleXNldHNcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IlhOQTZsT0oxVy03WFl2NVliNEFxbDc0b3hZSTdPSnFqT1FUNW9ZNDhxLWciLCJsb2dzIjoiU0t3RUk1NWN4M2pMdU16RWRZQ1NtZ1RSRWhmVjg2alQwVjd3WVdvZWxfQSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjZ9" + , "- - eyJoYXNoIjoiNWtxZ0tzWWtwbjZCcS1KUnhNYnA1VG9jUWhRWGRJVVNiM1NGQm1Ba3VWcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMFxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMTVUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMFxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHRpbWUgXFxcIjIxMDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMVxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMlxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24tdGVzdDAxXFxcIiAodGltZSBcXFwiMTkwMC0xMC0zMVQxODowMDowMFpcXFwiKSBcXFwiYWxsb2NhdGlvbi10ZXN0MDFcXFwiIDEwMDAwMDAuMClcXG4oY29pbi5jcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24tdGVzdDAyXFxcIiAyMDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtYWxsb2NhdGlvbnNcIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiI1a3FnS3NZa3BuNkJxLUpSeE1icDVUb2NRaFFYZElVU2IzU0ZCbUFrdVZzIiwibG9ncyI6IkRTeVFBWXBXSW9OazlOSUFNaTgzODRBTDhnSU9LRzc2Y1FaMzZNaGtkOGciLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo3fQ" + , "- - eyJoYXNoIjoiZjIxVTFJRnlRN0ZfLTRlTnR6QTFuWnZjQ0g5cnY5ZnZsNWM1a0RyNXFsdyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwiZTdmN1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIl0sXCJzZW5kZXIwNVwiOltcImYwOWQ4ZjYzOTRhZWE0MjVmZTY3ODNkODhjZDgxMzYzZDgwMTdmMTZhZmQzNzExYzU3NWJlMGY1Y2Q1YzliYjlcIl0sXCJzZW5kZXIwNFwiOltcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl0sXCJtdWx0aS0wMi0wMy0wNC1hbnlcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcIjNhOWRkNTMyZDczZGFjZTE5NWRiYjY0ZDFkYmE2NTcyZmI3ODNkMGZkZDMyNDY4NWUzMmZiZGEyZjg5Zjk5YTZcIixcIjQzZjJhZGIxZGUxOTIwMDBjYjM3NzdiYWNjN2Y5ODNiNjYxNGZkOWMxNzE1Y2Q0NGNkNDg0YjZkM2EwZDM0YzhcIixcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl19LFwic2VuZGVyMDlcIjpbXCJjNTlkOTg0MGIwYjY2MDkwODM2NTQ2YjdlYjRhNzM2MDYyNTc1MjdlYzhjMmI0ODIzMDBmZDIyOTI2NGIwN2U2XCJdLFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXFxuXFxuKGNvaW4uY29pbmJhc2UgXFxcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcXFwiIChyZWFkLWtleXNldCBcXFwiZTdmN1xcXCIpIDE1MC4wKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWdyYW50c05cIn0ifQ" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJmMjFVMUlGeVE3Rl8tNGVOdHpBMW5admNDSDlydjlmdmw1YzVrRHI1cWx3IiwibG9ncyI6Iks5dDlKdFBZc1hGQ0dhNk5TTEI2V2xJdTFZaEJqWG94bTFPZkY2Y0xFd00iLCJldmVudHMiOlt7InBhcmFtcyI6WyIiLCJzZW5kZXIwMCIsMTAwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMSIsMTEwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMiIsMTIwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMyIsMTMwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNCIsMTQwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNSIsMTUwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNiIsMTYwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNyIsMTcwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOCIsMTgwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOSIsMTkwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMC0wMSIsMTAxMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMi0wMy0wNC1hbnkiLDEyMzQwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiZTdmNzYzNGU5MjU1NDFmMzY4YjgyN2FkNWM3MjQyMTkwNTEwMGY2MjA1Mjg1YTc4YzE5ZDdiNGEzODcxMTgwNSIsMTUwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifV0sIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjh9" + , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" + , "transactionsHash: XndbRFLq7is65RKueTygrkIbP07oitsnNdv9f9Q-_Uo" + , "outputsHash: Pa_j87P-788AYgWVCTZCru_xQ5vcFmkWKXhARgTJphU" + , "payloadHash: b0gX32KDdIRRqSTDJgonbGzFKnlTDzOBULlXmoPiiEM" + , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" + , "" + ] diff --git a/src/Chainweb/BlockHeader/Genesis/FastTimedCPMNPayload.hs b/src/Chainweb/BlockHeader/Genesis/FastTimedCPM1to9Payload.hs similarity index 99% rename from src/Chainweb/BlockHeader/Genesis/FastTimedCPMNPayload.hs rename to src/Chainweb/BlockHeader/Genesis/FastTimedCPM1to9Payload.hs index 8f26a950ea..588a15718a 100644 --- a/src/Chainweb/BlockHeader/Genesis/FastTimedCPMNPayload.hs +++ b/src/Chainweb/BlockHeader/Genesis/FastTimedCPM1to9Payload.hs @@ -2,7 +2,7 @@ -- This module is auto-generated. DO NOT EDIT IT MANUALLY. -module Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload ( payloadBlock ) where +module Chainweb.BlockHeader.Genesis.FastTimedCPM1to9Payload ( payloadBlock ) where import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T diff --git a/src/Chainweb/BlockHeader/Genesis/MainnetKADPayload.hs b/src/Chainweb/BlockHeader/Genesis/Mainnet10to19Payload.hs similarity index 99% rename from src/Chainweb/BlockHeader/Genesis/MainnetKADPayload.hs rename to src/Chainweb/BlockHeader/Genesis/Mainnet10to19Payload.hs index 737ee8b653..7107142ce7 100644 --- a/src/Chainweb/BlockHeader/Genesis/MainnetKADPayload.hs +++ b/src/Chainweb/BlockHeader/Genesis/Mainnet10to19Payload.hs @@ -2,7 +2,7 @@ -- This module is auto-generated. DO NOT EDIT IT MANUALLY. -module Chainweb.BlockHeader.Genesis.MainnetKADPayload ( payloadBlock ) where +module Chainweb.BlockHeader.Genesis.Mainnet10to19Payload ( payloadBlock ) where import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T diff --git a/src/Chainweb/BlockHeader/Genesis/TestnetNPayload.hs b/src/Chainweb/BlockHeader/Genesis/Testnet1to19Payload.hs similarity index 99% rename from src/Chainweb/BlockHeader/Genesis/TestnetNPayload.hs rename to src/Chainweb/BlockHeader/Genesis/Testnet1to19Payload.hs index b150b5d158..647302f7fe 100644 --- a/src/Chainweb/BlockHeader/Genesis/TestnetNPayload.hs +++ b/src/Chainweb/BlockHeader/Genesis/Testnet1to19Payload.hs @@ -2,7 +2,7 @@ -- This module is auto-generated. DO NOT EDIT IT MANUALLY. -module Chainweb.BlockHeader.Genesis.TestnetNPayload ( payloadBlock ) where +module Chainweb.BlockHeader.Genesis.Testnet1to19Payload ( payloadBlock ) where import Data.Text.Encoding (encodeUtf8) import qualified Data.Text as T diff --git a/src/Chainweb/Chainweb/Configuration.hs b/src/Chainweb/Chainweb/Configuration.hs index 1de63311d1..4360938957 100644 --- a/src/Chainweb/Chainweb/Configuration.hs +++ b/src/Chainweb/Chainweb/Configuration.hs @@ -113,6 +113,7 @@ import Chainweb.Payload.RestAPI (PayloadBatchLimit(..), defaultServicePayloadBat import Chainweb.Utils import Chainweb.Version import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Mainnet import Chainweb.Version.Registry @@ -407,7 +408,7 @@ validateChainwebConfiguration c = do validateChainwebVersion (_configChainwebVersion c) validateChainwebVersion :: ConfigValidation ChainwebVersion [] -validateChainwebVersion v = unless (_versionCode v == _versionCode devnet) $ +validateChainwebVersion v = unless (_versionCode v `elem` map _versionCode [devnet, fastDevnet]) $ throwError $ T.unwords [ "Specifying version properties is only legal with chainweb-version" , "set to development, but version is set to" @@ -575,8 +576,11 @@ parseVersion = constructVersion maybe (_versionUpgrades winningVersion) (\fub' -> OnChains $ HM.mapWithKey (\cid _ -> - let fubHeight = winningVersion ^?! versionForks . at fub' . _Just . onChain cid - in HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid)) + case winningVersion ^?! versionForks . at fub' . _Just . onChain cid of + ForkNever -> error "the fork upper bound never occurs" + ForkAtBlockHeight fubHeight -> HM.filterWithKey (\bh _ -> bh <= fubHeight) (winningVersion ^?! versionUpgrades . onChain cid) + ForkAtGenesis -> winningVersion ^?! versionUpgrades . onChain cid + ) (HS.toMap (chainIds winningVersion)) ) fub & versionCheats . disablePow .~ disablePow' diff --git a/src/Chainweb/Pact/PactService/ExecBlock.hs b/src/Chainweb/Pact/PactService/ExecBlock.hs index 9433ac75af..e67dab17ad 100644 --- a/src/Chainweb/Pact/PactService/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/ExecBlock.hs @@ -413,7 +413,7 @@ applyPactCmd isGenesis env miner txTimeLimit cmd = StateT $ \(T2 mcache maybeBlo T2 result mcache' <- do pd <- getTxContext (publicMetaOf gasLimitedCmd) if isGenesis - then liftIO $! applyGenesisCmd logger env P.noSPVSupport gasLimitedCmd + then liftIO $! applyGenesisCmd logger env P.noSPVSupport pd gasLimitedCmd else do spv <- use psSpvSupport let diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index c397e84463..c47c988a95 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -258,10 +258,12 @@ applyGenesisCmd -- ^ Pact db environment -> SPVSupport -- ^ SPV support (validates cont proofs) + -> TxContext + -- ^ tx metadata -> Command (Payload PublicMeta ParsedCode) -- ^ command with payload to execute -> IO (T2 (CommandResult [TxLog Value]) ModuleCache) -applyGenesisCmd logger dbEnv spv cmd = +applyGenesisCmd logger dbEnv spv txCtx cmd = second _txCache <$!> runTransactionM tenv txst go where nid = networkIdOf cmd @@ -277,14 +279,14 @@ applyGenesisCmd logger dbEnv spv cmd = , _txGasPrice = 0.0 , _txRequestKey = rk , _txGasLimit = 0 - , _txExecutionConfig = mkExecutionConfig - [ FlagDisablePact40 - , FlagDisablePact420 - , FlagDisableInlineMemCheck - , FlagDisablePact43 - , FlagDisablePact44 - , FlagDisablePact45 - ] + , _txExecutionConfig = ExecutionConfig + $ flagsFor (ctxVersion txCtx) (ctxChainId txCtx) (_blockHeight $ ctxBlockHeader txCtx) + -- TODO this is very ugly. Genesis blocks need to install keysets + -- outside of namespaces so we need to disable Pact 4.4. It would be + -- preferable to have a flag specifically for the namespaced keyset + -- stuff so that we retain the super cow powers in genesis and + -- upgrade txs. + <> S.fromList [ FlagDisableInlineMemCheck, FlagDisablePact44 ] } txst = TransactionState { _txCache = mempty diff --git a/src/Chainweb/Pact/Types.hs b/src/Chainweb/Pact/Types.hs index dcf9d1c226..b6aed96d63 100644 --- a/src/Chainweb/Pact/Types.hs +++ b/src/Chainweb/Pact/Types.hs @@ -93,6 +93,7 @@ module Chainweb.Pact.Types , TxContext(..) , ctxToPublicData , ctxToPublicData' + , ctxBlockHeader , ctxCurrentBlockHeight , ctxChainId , ctxVersion @@ -342,7 +343,7 @@ execTransactionM tenv txst act data TxContext = TxContext { _tcParentHeader :: ParentHeader , _tcPublicMeta :: PublicMeta - } + } deriving Show -- -------------------------------------------------------------------- -- diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index f50cc088c2..ebc5bdbfc4 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -30,6 +30,10 @@ module Chainweb.Version ( -- * Properties of Chainweb Version Fork(..) + , ForkHeight(..) + , _ForkAtBlockHeight + , _ForkAtGenesis + , _ForkNever , VersionGenesis(..) , VersionCheats(..) , VersionDefaults(..) @@ -248,6 +252,12 @@ instance FromJSON Fork where instance FromJSONKey Fork where fromJSONKey = FromJSONKeyTextParser $ either fail return . eitherFromText +data ForkHeight = ForkAtBlockHeight !BlockHeight | ForkAtGenesis | ForkNever + deriving stock (Generic, Eq, Ord) + deriving anyclass (Hashable, NFData) + +makePrisms ''ForkHeight + newtype ChainwebVersionName = ChainwebVersionName { getChainwebVersionName :: T.Text } deriving stock (Generic, Eq, Ord) @@ -312,7 +322,7 @@ data ChainwebVersion -- ^ The textual name of the Version, used in almost all REST endpoints. , _versionGraphs :: Rule BlockHeight ChainGraph -- ^ The chain graphs in the history and at which block heights they apply. - , _versionForks :: HashMap Fork (ChainMap BlockHeight) + , _versionForks :: HashMap Fork (ChainMap ForkHeight) -- ^ The block heights on each chain to apply behavioral changes. -- Interpretation of these is up to the functions in -- `Chainweb.Version.Guards`. @@ -543,7 +553,7 @@ forkUpgrades v = OnChains . foldl' go (HM.empty <$ HS.toMap (chainIds v)) | cid <- HM.keys acc , Just upg <- [txsPerChain ^? onChain cid] , not (null $ _upgradeTransactions upg) || emptyUpgradeError fork - , let forkHeight = v ^?! versionForks . at fork . _Just . onChain cid + , ForkAtBlockHeight forkHeight <- [v ^?! versionForks . at fork . _Just . onChain cid] , forkHeight /= maxBound ] @@ -553,7 +563,7 @@ latestBehaviorAt :: ChainwebVersion -> BlockHeight latestBehaviorAt v = foldlOf' behaviorChanges max 0 v + 1 where behaviorChanges = fold - [ versionForks . folded . folded + [ versionForks . folded . folded . _ForkAtBlockHeight , versionUpgrades . folded . ifolded . asIndex , versionGraphs . to ruleHead . _1 . _Just - ] . filtered (/= maxBound) + ] diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index bad1a3bb5d..0a4a9cdc5f 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -20,8 +20,8 @@ import Chainweb.Utils.Rule import Chainweb.Version import qualified Chainweb.BlockHeader.Genesis.Development0Payload as DN0 -import qualified Chainweb.BlockHeader.Genesis.DevelopmentNPayload as DNN -import qualified Chainweb.BlockHeader.Genesis.DevelopmentKADPayload as DNKAD +import qualified Chainweb.BlockHeader.Genesis.Development1to9Payload as DNN +import qualified Chainweb.BlockHeader.Genesis.Development10to19Payload as DNKAD import qualified Chainweb.Pact.Transactions.DevelopmentTransactions as Devnet import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 @@ -41,29 +41,29 @@ devnet = ChainwebVersion , _versionName = ChainwebVersionName "development" , _versionForks = tabulateHashMap $ \case - SlowEpoch -> AllChains $ BlockHeight 0 - Vuln797Fix -> AllChains $ BlockHeight 0 - CoinV2 -> AllChains $ BlockHeight 1 - PactBackCompat_v16 -> AllChains $ BlockHeight 0 - ModuleNameFix -> AllChains $ BlockHeight 0 - SkipTxTimingValidation -> AllChains $ BlockHeight 0 - OldTargetGuard -> AllChains $ BlockHeight 0 - SkipFeatureFlagValidation -> AllChains $ BlockHeight 0 - ModuleNameFix2 -> AllChains $ BlockHeight 0 - OldDAGuard -> AllChains $ BlockHeight 1 - PactEvents -> AllChains $ BlockHeight 1 - SPVBridge -> AllChains $ BlockHeight 1 - Pact4Coin3 -> AllChains $ BlockHeight 2 - EnforceKeysetFormats -> AllChains $ BlockHeight 1 - Pact420 -> AllChains $ BlockHeight 1 - CheckTxHash -> AllChains $ BlockHeight 1 - Chainweb213Pact -> AllChains $ BlockHeight 3 - Chainweb214Pact -> AllChains $ BlockHeight 4 - Chainweb215Pact -> AllChains $ BlockHeight 5 - Pact44NewTrans -> AllChains $ BlockHeight 1 - Chainweb216Pact -> AllChains $ BlockHeight 6 - Chainweb217Pact -> AllChains $ BlockHeight 6 - Chainweb218Pact -> AllChains $ BlockHeight 6 + SlowEpoch -> AllChains $ ForkAtBlockHeight 0 + Vuln797Fix -> AllChains $ ForkAtBlockHeight 0 + CoinV2 -> AllChains $ ForkAtBlockHeight 1 + PactBackCompat_v16 -> AllChains $ ForkAtBlockHeight 0 + ModuleNameFix -> AllChains $ ForkAtBlockHeight 0 + SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight 0 + OldTargetGuard -> AllChains $ ForkAtBlockHeight 0 + SkipFeatureFlagValidation -> AllChains $ ForkAtBlockHeight 0 + ModuleNameFix2 -> AllChains $ ForkAtBlockHeight 0 + OldDAGuard -> AllChains $ ForkAtBlockHeight 1 + PactEvents -> AllChains $ ForkAtBlockHeight 1 + SPVBridge -> AllChains $ ForkAtBlockHeight 1 + Pact4Coin3 -> AllChains $ ForkAtBlockHeight 2 + EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight 1 + Pact420 -> AllChains $ ForkAtBlockHeight 1 + CheckTxHash -> AllChains $ ForkAtBlockHeight 1 + Chainweb213Pact -> AllChains $ ForkAtBlockHeight 3 + Chainweb214Pact -> AllChains $ ForkAtBlockHeight 4 + Chainweb215Pact -> AllChains $ ForkAtBlockHeight 5 + Pact44NewTrans -> AllChains $ ForkAtBlockHeight 1 + Chainweb216Pact -> AllChains $ ForkAtBlockHeight 6 + Chainweb217Pact -> AllChains $ ForkAtBlockHeight 6 + Chainweb218Pact -> AllChains $ ForkAtBlockHeight 6 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ forkUpgrades devnet diff --git a/src/Chainweb/Version/FastDevelopment.hs b/src/Chainweb/Version/FastDevelopment.hs new file mode 100644 index 0000000000..78015f4ad7 --- /dev/null +++ b/src/Chainweb/Version/FastDevelopment.hs @@ -0,0 +1,62 @@ +{-# language LambdaCase #-} +{-# language NumericUnderscores #-} +{-# language OverloadedStrings #-} +{-# language PatternSynonyms #-} +{-# language QuasiQuotes #-} +{-# language ViewPatterns #-} + +module Chainweb.Version.FastDevelopment(fastDevnet, pattern FastDevelopment) where + +import Chainweb.BlockCreationTime +import Chainweb.ChainId +import Chainweb.Difficulty +import Chainweb.Graph +import Chainweb.Time +import Chainweb.Utils +import Chainweb.Utils.Rule +import Chainweb.Version + +import qualified Chainweb.BlockHeader.Genesis.FastDevelopment0Payload as FDN0 +import qualified Chainweb.BlockHeader.Genesis.FastDevelopment1to19Payload as FDNN + +pattern FastDevelopment :: ChainwebVersion +pattern FastDevelopment <- ((== fastDevnet) -> True) where + FastDevelopment = fastDevnet + +fastDevnet :: ChainwebVersion +fastDevnet = ChainwebVersion + { _versionCode = ChainwebVersionCode 0x00000002 + , _versionName = ChainwebVersionName "fast-development" + + , _versionForks = tabulateHashMap $ \case + _ -> AllChains ForkAtGenesis + + , _versionUpgrades = AllChains mempty + + , _versionGraphs = + End twentyChainGraph + + , _versionBlockRate = BlockRate 30_000_000 + , _versionWindow = WindowWidth 120 + , _versionHeaderBaseSizeBytes = 318 - 110 + , _versionBootstraps = [] + , _versionGenesis = VersionGenesis + { _genesisBlockTarget = AllChains $ HashTarget (maxBound `div` 100_000) + , _genesisTime = AllChains $ BlockCreationTime [timeMicrosQQ| 2019-07-17T18:28:37.613832 |] + , _genesisBlockPayload = onChains $ concat + [ [(unsafeChainId 0, FDN0.payloadBlock)] + , [(unsafeChainId i, FDNN.payloadBlock) | i <- [1..19]] + ] + } + + , _versionMaxBlockGasLimit = End (Just 180_000) + , _versionCheats = VersionCheats + { _disablePow = True + , _fakeFirstEpochStart = True + , _disablePact = False + } + , _versionDefaults = VersionDefaults + { _disablePeerValidation = True + , _disableMempoolSync = False + } + } diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 4a3b3a6cf8..c3c0a2bfd6 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -60,14 +60,29 @@ import Chainweb.Transaction import Chainweb.Version import Chainweb.Utils.Rule -getForkHeight :: Fork -> ChainwebVersion -> ChainId -> BlockHeight +getForkHeight :: Fork -> ChainwebVersion -> ChainId -> ForkHeight getForkHeight fork v cid = v ^?! versionForks . at fork . _Just . onChain cid checkFork - :: (BlockHeight -> BlockHeight -> Bool) + :: (BlockHeight -> ForkHeight -> Bool) -> Fork -> ChainwebVersion -> ChainId -> BlockHeight -> Bool checkFork p f v cid h = p h (getForkHeight f v cid) +after :: BlockHeight -> ForkHeight -> Bool +after bh (ForkAtBlockHeight bh') = bh > bh' +after _ ForkAtGenesis = True +after _ ForkNever = False + +atOrAfter :: BlockHeight -> ForkHeight -> Bool +atOrAfter bh (ForkAtBlockHeight bh') = bh >= bh' +atOrAfter _ ForkAtGenesis = True +atOrAfter _ ForkNever = False + +before :: BlockHeight -> ForkHeight -> Bool +before bh (ForkAtBlockHeight bh') = bh < bh' +before _ ForkAtGenesis = False +before _ ForkNever = True + -- -------------------------------------------------------------------------- -- -- Header Validation Guards -- @@ -113,7 +128,7 @@ slowEpochGuard -> BlockHeight -- ^ BlockHeight of parent Header -> Bool -slowEpochGuard = checkFork (<) SlowEpoch +slowEpochGuard = checkFork before SlowEpoch -- | Use the current block time for computing epoch start date and -- target. @@ -123,7 +138,7 @@ slowEpochGuard = checkFork (<) SlowEpoch -- are marginal. -- oldTargetGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -oldTargetGuard = checkFork (<) OldTargetGuard +oldTargetGuard = checkFork before OldTargetGuard -- | Skip validation of feature flags. -- @@ -133,20 +148,20 @@ oldTargetGuard = checkFork (<) OldTargetGuard -- historical blocks for which both the Nonce and Flags could be anything. -- skipFeatureFlagValidationGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -skipFeatureFlagValidationGuard = checkFork (<) SkipFeatureFlagValidation +skipFeatureFlagValidationGuard = checkFork before SkipFeatureFlagValidation oldDaGuard :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -oldDaGuard = checkFork (<) OldDAGuard +oldDaGuard = checkFork before OldDAGuard ----------------- -- Payload validation guards vuln797Fix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -vuln797Fix = checkFork (>=) Vuln797Fix +vuln797Fix = checkFork atOrAfter Vuln797Fix -- | Preserve Pact bugs pre-1.6 chainweb. pactBackCompat_v16 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pactBackCompat_v16 = checkFork (<) PactBackCompat_v16 +pactBackCompat_v16 = checkFork before PactBackCompat_v16 -- | Early versions of chainweb used the creation time of the current header -- for validation of pact tx creation time and TTL. Nowadays the times of @@ -155,62 +170,62 @@ pactBackCompat_v16 = checkFork (<) PactBackCompat_v16 -- When this guard is enabled timing validation is skipped. -- skipTxTimingValidation :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -skipTxTimingValidation = checkFork (<) SkipTxTimingValidation +skipTxTimingValidation = checkFork before SkipTxTimingValidation -- | Checks height after which module name fix in effect. -- enableModuleNameFix :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -enableModuleNameFix = checkFork (>=) ModuleNameFix +enableModuleNameFix = checkFork atOrAfter ModuleNameFix -- | Related, later fix (Pact #801). -- enableModuleNameFix2 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -enableModuleNameFix2 = checkFork (>=) ModuleNameFix2 +enableModuleNameFix2 = checkFork atOrAfter ModuleNameFix2 -- | Turn on pact events in command output. enablePactEvents :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -enablePactEvents = checkFork (>=) PactEvents +enablePactEvents = checkFork atOrAfter PactEvents -- | Bridge support: ETH and event SPV. enableSPVBridge :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -enableSPVBridge = checkFork (>=) SPVBridge +enableSPVBridge = checkFork atOrAfter SPVBridge enforceKeysetFormats :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -enforceKeysetFormats = checkFork (>=) EnforceKeysetFormats +enforceKeysetFormats = checkFork atOrAfter EnforceKeysetFormats doCheckTxHash :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -doCheckTxHash = checkFork (>=) CheckTxHash +doCheckTxHash = checkFork atOrAfter CheckTxHash -- | Fork for musl trans funs pact44NewTrans :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pact44NewTrans = checkFork (>=) Pact44NewTrans +pact44NewTrans = checkFork atOrAfter Pact44NewTrans pact4Coin3 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pact4Coin3 = checkFork (>) Pact4Coin3 +pact4Coin3 = checkFork after Pact4Coin3 pact420 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pact420 = checkFork (>=) Pact420 +pact420 = checkFork atOrAfter Pact420 chainweb213Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb213Pact = checkFork (>=) Chainweb213Pact +chainweb213Pact = checkFork atOrAfter Chainweb213Pact chainweb214Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb214Pact = checkFork (>) Chainweb214Pact +chainweb214Pact = checkFork after Chainweb214Pact chainweb215Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb215Pact = checkFork (>) Chainweb215Pact +chainweb215Pact = checkFork after Chainweb215Pact chainweb216Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb216Pact = checkFork (>) Chainweb216Pact +chainweb216Pact = checkFork after Chainweb216Pact chainweb217Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb217Pact = checkFork (>) Chainweb217Pact +chainweb217Pact = checkFork after Chainweb217Pact cleanModuleCache :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -cleanModuleCache = checkFork (==) Chainweb217Pact +cleanModuleCache = checkFork atOrAfter Chainweb217Pact chainweb218Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -chainweb218Pact = checkFork (>=) Chainweb218Pact +chainweb218Pact = checkFork atOrAfter Chainweb218Pact pactParserVersion :: ChainwebVersion -> ChainId -> BlockHeight -> PactParserVersion pactParserVersion v cid bh diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index f1ae0295bf..91cd61f972 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -31,7 +31,7 @@ import qualified Chainweb.BlockHeader.Genesis.Mainnet6Payload as MN6 import qualified Chainweb.BlockHeader.Genesis.Mainnet7Payload as MN7 import qualified Chainweb.BlockHeader.Genesis.Mainnet8Payload as MN8 import qualified Chainweb.BlockHeader.Genesis.Mainnet9Payload as MN9 -import qualified Chainweb.BlockHeader.Genesis.MainnetKADPayload as MNKAD +import qualified Chainweb.BlockHeader.Genesis.Mainnet10to19Payload as MNKAD import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 import qualified Chainweb.Pact.Transactions.CoinV5Transactions as CoinV5 @@ -93,51 +93,51 @@ mainnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000005 , _versionName = ChainwebVersionName "mainnet01" , _versionForks = tabulateHashMap $ \case - SlowEpoch -> AllChains (BlockHeight 80_000) + SlowEpoch -> AllChains (ForkAtBlockHeight $ BlockHeight 80_000) Vuln797Fix -> onChains $ - [ (unsafeChainId 0, BlockHeight 121_452) -- 2019-12-10T21:00:00.0 - , (unsafeChainId 1, BlockHeight 121_452) - , (unsafeChainId 2, BlockHeight 121_452) - , (unsafeChainId 3, BlockHeight 121_451) - , (unsafeChainId 4, BlockHeight 121_451) - , (unsafeChainId 5, BlockHeight 121_452) - , (unsafeChainId 6, BlockHeight 121_452) - , (unsafeChainId 7, BlockHeight 121_451) - , (unsafeChainId 8, BlockHeight 121_452) - , (unsafeChainId 9, BlockHeight 121_451) - ] <> [(unsafeChainId i, BlockHeight 0) | i <- [10..19]] + [ (unsafeChainId 0, ForkAtBlockHeight $ BlockHeight 121_452) -- 2019-12-10T21:00:00.0 + , (unsafeChainId 1, ForkAtBlockHeight $ BlockHeight 121_452) + , (unsafeChainId 2, ForkAtBlockHeight $ BlockHeight 121_452) + , (unsafeChainId 3, ForkAtBlockHeight $ BlockHeight 121_451) + , (unsafeChainId 4, ForkAtBlockHeight $ BlockHeight 121_451) + , (unsafeChainId 5, ForkAtBlockHeight $ BlockHeight 121_452) + , (unsafeChainId 6, ForkAtBlockHeight $ BlockHeight 121_452) + , (unsafeChainId 7, ForkAtBlockHeight $ BlockHeight 121_451) + , (unsafeChainId 8, ForkAtBlockHeight $ BlockHeight 121_452) + , (unsafeChainId 9, ForkAtBlockHeight $ BlockHeight 121_451) + ] <> [(unsafeChainId i, ForkAtGenesis) | i <- [10..19]] CoinV2 -> onChains $ - [ (unsafeChainId 0, BlockHeight 140_808) - , (unsafeChainId 1, BlockHeight 140_809) - , (unsafeChainId 2, BlockHeight 140_808) - , (unsafeChainId 3, BlockHeight 140_809) - , (unsafeChainId 4, BlockHeight 140_808) - , (unsafeChainId 5, BlockHeight 140_808) - , (unsafeChainId 6, BlockHeight 140_808) - , (unsafeChainId 7, BlockHeight 140_809) - , (unsafeChainId 8, BlockHeight 140_808) - , (unsafeChainId 9, BlockHeight 140_808) - ] <> [(unsafeChainId i, BlockHeight 1) | i <- [10..19]] - PactBackCompat_v16 -> AllChains (BlockHeight 328_000) - ModuleNameFix -> AllChains (BlockHeight 448_501) - SkipTxTimingValidation -> AllChains (BlockHeight 449_940) - OldTargetGuard -> AllChains (BlockHeight 452_820) -- ~ 2020-04-04T00:00:00Z - SkipFeatureFlagValidation -> AllChains (BlockHeight 530_500) -- ~ 2020-05-01T00:00:xxZ - ModuleNameFix2 -> AllChains (BlockHeight 752_214) - OldDAGuard -> AllChains (BlockHeight 771_414) -- ~ 2020-07-23 16:00:00 - PactEvents -> AllChains (BlockHeight 1_138_000) - SPVBridge -> AllChains (BlockHeight 1_275_000) - Pact4Coin3 -> AllChains (BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 - EnforceKeysetFormats -> AllChains (BlockHeight 2_162_000) -- 2022-01-17T17:51:12 - Pact420 -> AllChains (BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 - CheckTxHash -> AllChains (BlockHeight 2_349_800) -- 2022-01-23T02:53:38 - Chainweb213Pact -> AllChains (BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 - Chainweb214Pact -> AllChains (BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 - Chainweb215Pact -> AllChains (BlockHeight 2_766_630) -- 2022-06-17T00:00:00+00:00 - Pact44NewTrans -> AllChains (BlockHeight 2_965_885) -- Todo: add date - Chainweb216Pact -> AllChains (BlockHeight 2_988_324) -- 2022-09-02T00:00:00+00:00 - Chainweb217Pact -> AllChains (BlockHeight 3_250_348) -- 2022-12-02T00:00:00+00:00 - Chainweb218Pact -> AllChains (BlockHeight 3_512_363) -- 2023-03-03 00:00:00+00:00 + [ (unsafeChainId 0, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 1, ForkAtBlockHeight $ BlockHeight 140_809) + , (unsafeChainId 2, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 3, ForkAtBlockHeight $ BlockHeight 140_809) + , (unsafeChainId 4, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 5, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 6, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 7, ForkAtBlockHeight $ BlockHeight 140_809) + , (unsafeChainId 8, ForkAtBlockHeight $ BlockHeight 140_808) + , (unsafeChainId 9, ForkAtBlockHeight $ BlockHeight 140_808) + ] <> [(unsafeChainId i, ForkAtGenesis) | i <- [10..19]] + PactBackCompat_v16 -> AllChains (ForkAtBlockHeight $ BlockHeight 328_000) + ModuleNameFix -> AllChains (ForkAtBlockHeight $ BlockHeight 448_501) + SkipTxTimingValidation -> AllChains (ForkAtBlockHeight $ BlockHeight 449_940) + OldTargetGuard -> AllChains (ForkAtBlockHeight $ BlockHeight 452_820) -- ~ 2020-04-04T00:00:00Z + SkipFeatureFlagValidation -> AllChains (ForkAtBlockHeight $ BlockHeight 530_500) -- ~ 2020-05-01T00:00:xxZ + ModuleNameFix2 -> AllChains (ForkAtBlockHeight $ BlockHeight 752_214) + OldDAGuard -> AllChains (ForkAtBlockHeight $ BlockHeight 771_414) -- ~ 2020-07-23 16:00:00 + PactEvents -> AllChains (ForkAtBlockHeight $ BlockHeight 1_138_000) + SPVBridge -> AllChains (ForkAtBlockHeight $ BlockHeight 1_275_000) + Pact4Coin3 -> AllChains (ForkAtBlockHeight $ BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 + EnforceKeysetFormats -> AllChains (ForkAtBlockHeight $ BlockHeight 2_162_000) -- 2022-01-17T17:51:12 + Pact420 -> AllChains (ForkAtBlockHeight $ BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 + CheckTxHash -> AllChains (ForkAtBlockHeight $ BlockHeight 2_349_800) -- 2022-01-23T02:53:38 + Chainweb213Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 + Chainweb214Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 + Chainweb215Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_766_630) -- 2022-06-17T00:00:00+00:00 + Pact44NewTrans -> AllChains (ForkAtBlockHeight $ BlockHeight 2_965_885) -- Todo: add date + Chainweb216Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_988_324) -- 2022-09-02T00:00:00+00:00 + Chainweb217Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 3_250_348) -- 2022-12-02T00:00:00+00:00 + Chainweb218Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 3_512_363) -- 2023-03-03 00:00:00+00:00 , _versionGraphs = (to20ChainsMainnet, twentyChainGraph) `Above` @@ -146,7 +146,7 @@ mainnet = ChainwebVersion , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = - (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` + (succ $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 180_000) `Above` End Nothing , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts , _versionGenesis = VersionGenesis diff --git a/src/Chainweb/Version/Registry.hs b/src/Chainweb/Version/Registry.hs index f1afbb1fad..ee1c9c7dfd 100644 --- a/src/Chainweb/Version/Registry.hs +++ b/src/Chainweb/Version/Registry.hs @@ -38,6 +38,7 @@ import GHC.Stack import Chainweb.Version import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Mainnet import Chainweb.Version.Testnet import Chainweb.Utils.Rule @@ -83,9 +84,7 @@ validateVersion v = do , hasAllChains (_genesisTime $ _versionGenesis v) ])] ] - if null errors - then return () - else + unless (null errors) $ error $ unlines $ ["errors encountered validating version " <> show v <> ":"] <> errors -- | Look up a version in the registry by code. @@ -106,11 +105,12 @@ lookupVersionByCode code return $ fromMaybe (error notRegistered) $ HM.lookup code m notRegistered | code == _versionCode devnet = "devnet version used but not registered, remember to do so after it's configured" + | code == _versionCode fastDevnet = "fastDevnet version used but not registered, remember to do so after it's configured" | otherwise = "version not registered with code " <> show code <> ", have you seen Chainweb.Test.TestVersions.legalizeTestVersion?" -- | Versions known to us by name. knownVersions :: [ChainwebVersion] -knownVersions = [mainnet, testnet, devnet] +knownVersions = [mainnet, testnet, devnet, fastDevnet] -- | Look up a known version by name, usually with `m` instantiated to some -- configuration parser monad. diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index 30ceafd083..17f51d19bb 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -36,7 +36,7 @@ import qualified Chainweb.Pact.Transactions.Mainnet8Transactions as MN8 import qualified Chainweb.Pact.Transactions.Mainnet9Transactions as MN9 import qualified Chainweb.Pact.Transactions.MainnetKADTransactions as MNKAD import qualified Chainweb.BlockHeader.Genesis.Testnet0Payload as PN0 -import qualified Chainweb.BlockHeader.Genesis.TestnetNPayload as PNN +import qualified Chainweb.BlockHeader.Genesis.Testnet1to19Payload as PNN -- | Initial hash target for testnet 20-chain transition. Based on the following -- header from devnet running with 5 GPUs hash power. Using this target unchanged @@ -92,32 +92,32 @@ testnet = ChainwebVersion { _versionCode = ChainwebVersionCode 0x00000007 , _versionName = ChainwebVersionName "testnet04" , _versionForks = tabulateHashMap $ \case - SlowEpoch -> AllChains 0 - Vuln797Fix -> AllChains 0 + SlowEpoch -> AllChains ForkAtGenesis + Vuln797Fix -> AllChains ForkAtGenesis CoinV2 -> onChains $ concat - [ [(unsafeChainId i, BlockHeight 1) | i <- [0..9]] - , [(unsafeChainId i, BlockHeight 337_000) | i <- [10..19]] + [ [(unsafeChainId i, ForkAtBlockHeight $ BlockHeight 1) | i <- [0..9]] + , [(unsafeChainId i, ForkAtBlockHeight $ BlockHeight 337_000) | i <- [10..19]] ] - PactBackCompat_v16 -> AllChains 0 - ModuleNameFix -> AllChains 2 - SkipTxTimingValidation -> AllChains 1 - OldTargetGuard -> AllChains 0 - SkipFeatureFlagValidation -> AllChains 0 - ModuleNameFix2 -> AllChains 289_966 -- ~ 2020-07-13 - OldDAGuard -> AllChains 318_204 -- ~ 2020-07-23 16:00:00 - PactEvents -> AllChains 660_000 - SPVBridge -> AllChains 820_000 -- 2021-01-14T17:12:02 - Pact4Coin3 -> AllChains 1_261_000 -- 2021-06-17T15:54:14 - EnforceKeysetFormats -> AllChains 1_701_000 -- 2021-11-18T17:54:36 - Pact420 -> AllChains 1_862_000 -- 2021-06-19T03:34:05 - CheckTxHash -> AllChains 1_889_000 -- 2022-01-24T04:19:24 - Chainweb213Pact -> AllChains 1_974_556 -- 2022-02-25 00:00:00 - Chainweb214Pact -> AllChains 2_134_331 -- 2022-04-21T12:00:00Z - Chainweb215Pact -> AllChains 2_295_437 -- 2022-06-16T12:00:00+00:00 - Pact44NewTrans -> AllChains 2_500_369 -- Todo: add date - Chainweb216Pact -> AllChains 2_516_739 -- 2022-09-01 12:00:00+00:00 - Chainweb217Pact -> AllChains 2_777_367 -- 2022-12-01 12:00:00+00:00 - Chainweb218Pact -> AllChains 3_038_343 -- 2023-03-02 12:00:00+00:00 + PactBackCompat_v16 -> AllChains $ ForkAtBlockHeight $ BlockHeight 0 + ModuleNameFix -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 + SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 + OldTargetGuard -> AllChains $ ForkAtBlockHeight $ BlockHeight 0 + SkipFeatureFlagValidation -> AllChains $ ForkAtBlockHeight $ BlockHeight 0 + ModuleNameFix2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 289_966 -- ~ 2020-07-13 + OldDAGuard -> AllChains $ ForkAtBlockHeight $ BlockHeight 318_204 -- ~ 2020-07-23 16:00:00 + PactEvents -> AllChains $ ForkAtBlockHeight $ BlockHeight 660_000 + SPVBridge -> AllChains $ ForkAtBlockHeight $ BlockHeight 820_000 -- 2021-01-14T17:12:02 + Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_261_000 -- 2021-06-17T15:54:14 + EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_701_000 -- 2021-11-18T17:54:36 + Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_862_000 -- 2021-06-19T03:34:05 + CheckTxHash -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_889_000 -- 2022-01-24T04:19:24 + Chainweb213Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_974_556 -- 2022-02-25 00:00:00 + Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_134_331 -- 2022-04-21T12:00:00Z + Chainweb215Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_295_437 -- 2022-06-16T12:00:00+00:00 + Pact44NewTrans -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_500_369 -- Todo: add date + Chainweb216Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_516_739 -- 2022-09-01 12:00:00+00:00 + Chainweb217Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_777_367 -- 2022-12-01 12:00:00+00:00 + Chainweb218Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 3_038_343 -- 2023-03-02 12:00:00+00:00 , _versionGraphs = (to20ChainsTestnet, twentyChainGraph) `Above` @@ -126,7 +126,7 @@ testnet = ChainwebVersion , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = - (succ $ testnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0), Just 180_000) `Above` + (succ $ testnet ^?! versionForks . at Chainweb216Pact . _Just . onChain (unsafeChainId 0) . _ForkAtBlockHeight, Just 180_000) `Above` End Nothing , _versionBootstraps = domainAddr2PeerInfo testnetBootstrapHosts , _versionGenesis = VersionGenesis diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index a5d4faedc2..6977677133 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -336,7 +336,7 @@ blockCoinV2RemediationTests _ envIo = _ -> assertFailure $ "coin v2 remediation block should have at least 3 transactions:" ++ " coinbase + 2 remediations" where - bhCoinV2Rem = v ^?! versionForks . at CoinV2 . _Just . onChain cid . to getBlockHeight + bhCoinV2Rem = v ^?! versionForks . at CoinV2 . _Just . onChain cid . _ForkAtBlockHeight . to getBlockHeight req h = BlockReq nid $ PartialBlockId (Just h) Nothing block20ChainRemediationTests :: RosettaTest @@ -411,7 +411,7 @@ blockCoinV3RemediationTests _ envIo = _ -> assertFailure $ "coin v3 remediation block should have at least 3 transactions:" ++ " coinbase + 2 remediations" where - bhCoinV3Rem = v ^?! versionForks . at Pact4Coin3 . _Just . onChain cid . to getBlockHeight + bhCoinV3Rem = v ^?! versionForks . at Pact4Coin3 . _Just . onChain cid . _ForkAtBlockHeight . to getBlockHeight req h = BlockReq nid $ PartialBlockId (Just h) Nothing -- | Rosetta construction endpoints tests (i.e. tx formatting and submission) diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index b28e1de991..997cea8f1d 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE BlockArguments #-} {-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NumericUnderscores #-} @@ -18,7 +19,7 @@ import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS import qualified Data.List as List import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM0Payload as TN0 -import qualified Chainweb.BlockHeader.Genesis.FastTimedCPMNPayload as TNN +import qualified Chainweb.BlockHeader.Genesis.FastTimedCPM1to9Payload as TNN import System.IO.Unsafe @@ -73,7 +74,7 @@ buildTestVersion f = where v = f v --- | All testing `ChainwebVersion`s *must* have unique names and must be +-- | All testing `ChainwebVersion`s *must* have unique names and *must* be -- included in this list to be assigned a version code, and also registered via -- `buildTestVersion` into the global version registry. Failure to do so will -- result in runtime errors from `Chainweb.Version.Registry`. @@ -110,31 +111,31 @@ testVersionTemplate v = v & versionBootstraps .~ [testBootstrapPeerInfos] -- | A set of fork heights which are relatively fast, but not fast enough to break anything. -fastForks :: HashMap Fork (ChainMap BlockHeight) +fastForks :: HashMap Fork (ChainMap ForkHeight) fastForks = tabulateHashMap $ \case - Pact420 -> AllChains (BlockHeight 0) - SlowEpoch -> AllChains (BlockHeight 0) - OldTargetGuard -> AllChains (BlockHeight 0) - SkipFeatureFlagValidation -> AllChains (BlockHeight 0) - OldDAGuard -> AllChains (BlockHeight 0) - Vuln797Fix -> AllChains (BlockHeight 0) - PactBackCompat_v16 -> AllChains (BlockHeight 0) - SPVBridge -> AllChains (BlockHeight 0) - EnforceKeysetFormats -> AllChains (BlockHeight 0) - CheckTxHash -> AllChains (BlockHeight 0) - Pact44NewTrans -> AllChains (BlockHeight 0) - Chainweb213Pact -> AllChains (BlockHeight 0) - PactEvents -> AllChains (BlockHeight 0) - CoinV2 -> AllChains (BlockHeight 1) - SkipTxTimingValidation -> AllChains (BlockHeight 2) - ModuleNameFix -> AllChains (BlockHeight 2) - ModuleNameFix2 -> AllChains (BlockHeight 2) - Pact4Coin3 -> AllChains (BlockHeight 4) - Chainweb214Pact -> AllChains (BlockHeight 5) - Chainweb215Pact -> AllChains (BlockHeight 10) - Chainweb216Pact -> AllChains (BlockHeight 11) - Chainweb217Pact -> AllChains (BlockHeight 20) - Chainweb218Pact -> AllChains (BlockHeight 20) + SlowEpoch -> AllChains ForkAtGenesis + OldTargetGuard -> AllChains ForkAtGenesis + SkipFeatureFlagValidation -> AllChains ForkAtGenesis + OldDAGuard -> AllChains ForkAtGenesis + Vuln797Fix -> AllChains ForkAtGenesis + PactBackCompat_v16 -> AllChains ForkAtGenesis + SPVBridge -> AllChains ForkAtGenesis + EnforceKeysetFormats -> AllChains ForkAtGenesis + CheckTxHash -> AllChains ForkAtGenesis + Pact44NewTrans -> AllChains ForkAtGenesis + Chainweb213Pact -> AllChains ForkAtGenesis + PactEvents -> AllChains ForkAtGenesis + CoinV2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 + Pact420 -> AllChains ForkNever + SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 + ModuleNameFix -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 + ModuleNameFix2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 + Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 4 + Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 5 + Chainweb215Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 10 + Chainweb216Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 11 + Chainweb217Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 20 + Chainweb218Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 20 -- | A test version without Pact or PoW, with only one chain graph. barebonesTestVersion :: ChainGraph -> ChainwebVersion @@ -158,7 +159,7 @@ barebonesTestVersion g = buildTestVersion $ \v -> , _genesisBlockTarget = AllChains maxTarget , _genesisTime = AllChains $ BlockCreationTime epoch } - & versionForks .~ HM.fromList [ (f, AllChains $ BlockHeight 0) | f <- [minBound..maxBound] ] + & versionForks .~ HM.fromList [ (f, AllChains ForkAtGenesis) | f <- [minBound..maxBound] ] & versionUpgrades .~ AllChains HM.empty -- | A test version without Pact or PoW, with a chain graph upgrade at block height 8. @@ -169,9 +170,9 @@ timedConsensusVersion g1 g2 = buildTestVersion $ \v -> v & versionBlockRate .~ BlockRate 1_000_000 & versionWindow .~ WindowWidth 120 & versionForks .~ tabulateHashMap (\case - SkipTxTimingValidation -> AllChains (BlockHeight 2) + SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight (BlockHeight 2) -- pact is disabled, we don't care about pact forks - _ -> AllChains (BlockHeight 0) + _ -> AllChains ForkAtGenesis ) & versionUpgrades .~ AllChains HM.empty & versionGraphs .~ Above (BlockHeight 8, g2) (End g1) @@ -229,31 +230,30 @@ slowForkingCpmTestVersion :: ChainGraph -> ChainwebVersion slowForkingCpmTestVersion g = buildTestVersion $ \v -> v & cpmTestVersion g & versionName .~ ChainwebVersionName ("slowfork-CPM-" <> toText g) - & versionForks .~ HM.fromList - [ (SlowEpoch, AllChains (BlockHeight 0)) - , (OldTargetGuard, AllChains (BlockHeight 0)) - , (SkipFeatureFlagValidation, AllChains (BlockHeight 0)) - , (OldDAGuard, AllChains (BlockHeight 0)) - , (Vuln797Fix, AllChains (BlockHeight 0)) - , (PactBackCompat_v16, AllChains (BlockHeight 0)) - , (SPVBridge, AllChains (BlockHeight 0)) - , (Pact44NewTrans, AllChains (BlockHeight 0)) - , (CoinV2, AllChains (BlockHeight 1)) - , (SkipTxTimingValidation, AllChains (BlockHeight 2)) - , (ModuleNameFix, AllChains (BlockHeight 2)) - , (ModuleNameFix2, AllChains (BlockHeight 2)) - , (Pact420, AllChains (BlockHeight 5)) - , (CheckTxHash, AllChains (BlockHeight 7)) - , (EnforceKeysetFormats, AllChains (BlockHeight 10)) - , (PactEvents, AllChains (BlockHeight 10)) - , (Pact4Coin3, AllChains (BlockHeight 20)) - , (Chainweb213Pact, AllChains (BlockHeight 26)) - , (Chainweb214Pact, AllChains (BlockHeight 30)) - , (Chainweb215Pact, AllChains (BlockHeight 35)) - , (Chainweb216Pact, AllChains (BlockHeight 53)) - , (Chainweb217Pact, AllChains (BlockHeight 55)) - , (Chainweb218Pact, AllChains (BlockHeight 60)) - ] + & versionForks .~ tabulateHashMap \case + SlowEpoch -> AllChains ForkAtGenesis + OldTargetGuard -> AllChains ForkAtGenesis + SkipFeatureFlagValidation -> AllChains ForkAtGenesis + OldDAGuard -> AllChains ForkAtGenesis + Vuln797Fix -> AllChains ForkAtGenesis + PactBackCompat_v16 -> AllChains ForkAtGenesis + SPVBridge -> AllChains ForkAtGenesis + Pact44NewTrans -> AllChains ForkAtGenesis + CoinV2 -> AllChains $ ForkAtBlockHeight (BlockHeight 1) + SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight (BlockHeight 2) + ModuleNameFix -> AllChains $ ForkAtBlockHeight (BlockHeight 2) + ModuleNameFix2 -> AllChains $ ForkAtBlockHeight (BlockHeight 2) + Pact420 -> AllChains $ ForkAtBlockHeight (BlockHeight 5) + CheckTxHash -> AllChains $ ForkAtBlockHeight (BlockHeight 7) + EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight (BlockHeight 10) + PactEvents -> AllChains $ ForkAtBlockHeight (BlockHeight 10) + Pact4Coin3 -> AllChains $ ForkAtBlockHeight (BlockHeight 20) + Chainweb213Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 26) + Chainweb214Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 30) + Chainweb215Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 35) + Chainweb216Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 53) + Chainweb217Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 55) + Chainweb218Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 60) -- | CPM version (see `cpmTestVersion`) with forks and upgrades quickly enabled. fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion @@ -268,5 +268,5 @@ noBridgeCpmTestVersion :: ChainGraph -> ChainwebVersion noBridgeCpmTestVersion g = buildTestVersion $ \v -> v & cpmTestVersion g & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) - & versionForks .~ (fastForks & at SPVBridge ?~ AllChains maxBound) + & versionForks .~ (fastForks & at SPVBridge ?~ AllChains ForkNever) diff --git a/test/ChainwebTests.hs b/test/ChainwebTests.hs index d33511f4fa..4f4b0a457c 100644 --- a/test/ChainwebTests.hs +++ b/test/ChainwebTests.hs @@ -64,6 +64,7 @@ import Chainweb.Test.Utils import qualified Chainweb.Test.Version (tests) import qualified Chainweb.Test.Chainweb.Utils.Paging (properties) import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Registry import Chainweb.Storage.Table.RocksDB @@ -77,6 +78,7 @@ import qualified P2P.Test.Node (properties) main :: IO () main = do registerVersion Development + registerVersion FastDevelopment withTempRocksDb "chainweb-tests" $ \rdb -> withToyDB rdb toyChainId $ \h0 db -> defaultMainWithIngredients (consoleAndJsonReporter : defaultIngredients) diff --git a/tools/cwtool/CwTool.hs b/tools/cwtool/CwTool.hs index d80f8199cc..d7f3cb915b 100644 --- a/tools/cwtool/CwTool.hs +++ b/tools/cwtool/CwTool.hs @@ -2,6 +2,7 @@ module Main where import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Registry import System.Environment @@ -22,6 +23,7 @@ import qualified TxSimulator main :: IO () main = do registerVersion Development + registerVersion FastDevelopment args <- getArgs case args of [] -> printHelp topLevelCommands diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index 37fe0370e4..e25e9bae8b 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} @@ -27,6 +26,7 @@ module Ea ( main ) where +import Control.Exception import Control.Lens import Data.Foldable @@ -38,6 +38,7 @@ import qualified Data.Text.IO as TIO import Data.Traversable import qualified Data.Vector as V import qualified Data.Yaml as Yaml +import GHC.Exts(the) import Ea.Genesis @@ -74,11 +75,12 @@ import Pact.Types.Command hiding (Payload) main :: IO () main = void $ do devnet + fastDevnet fastnet testnet mainnet genTxModules - gen20ChainPayloads + -- gen20ChainPayloads genCoinV3Payloads genCoinV4Payloads genCoinV5Payloads @@ -87,6 +89,11 @@ main = void $ do devnet = mkPayloads [ development0 , developmentN + , developmentKAD + ] + fastDevnet = mkPayloads + [ fastDevelopment0 + , fastDevelopmentN ] fastnet = mkPayloads [fastTimedCPM0, fastTimedCPMN] testnet = mkPayloads [testnet0, testnetN] @@ -101,61 +108,67 @@ main = void $ do , mainnet7 , mainnet8 , mainnet9 + , mainnetKAD ] -show_ :: GChainId -> String -show_ = \case - N -> "all chains" - KAD -> "chains 10-19" - n -> "Chain " <> show n +show_ :: ChainIdRange -> String +show_ (ChainIdRange n n') + | n == n' = "Chain " <> show n + | otherwise = "Chains " <> show n <> "-" <> show n' + +fullGenesisTag :: Genesis -> Text +fullGenesisTag (Genesis _ tag cidr _ _ _ _ _) = tag <> T.pack (chainIdRangeTag cidr) --- | Generate paylaods for a traversable of txs +-- | Generate payloads for a traversable of txs -- mkPayloads :: Traversable t => t Genesis -> IO () -mkPayloads = traverse_ mkPayload +mkPayloads = traverse_ (\g -> writePayload g =<< mkPayload g) --- | Generate a payload for a given genesis transaction +writePayload :: Genesis -> Text -> IO () +writePayload gen@(Genesis v tag cid c k a ns cc) payload = do + let fileName = "src/Chainweb/BlockHeader/Genesis/" <> fullGenesisTag gen <> "Payload.hs" + TIO.writeFile (T.unpack fileName) payload + +-- | Generate a payload for a given list of genesis transactions -- -mkPayload :: Genesis -> IO () -mkPayload (Genesis v tag cid c k a ns) = do - printf ("Generating Genesis Payload for %s on " <> show_ cid <> "...\n") $ show v - genPayloadModule v (tag <> sshow cid) txs +mkPayload :: Genesis -> IO Text +mkPayload gen@(Genesis v tag cidr@(ChainIdRange l u) c k a ns cc) = do + printf ("Generating Genesis Payload for %s on " <> show_ cidr <> "...\n") $ show v + payloadModules <- for [l..u] $ \cid -> + genPayloadModule v (fullGenesisTag gen) (unsafeChainId cid) =<< mkChainwebTxs txs + evaluate $ the payloadModules where - -- coin contract genesis txs - cc :: [FilePath] - cc = [fungibleAssetV1, coinContractV1, gasPayer] -- final tx list. -- NB: this is position-sensitive data. txs :: [FilePath] txs = cc <> toList ns <> toList k <> toList a <> toList c +-- gen20ChainPayloads :: IO () +-- gen20ChainPayloads = traverse_ mk20ChainPayload [developmentKAD, mainnetKAD] +-- where +-- mk20ChainPayload (Genesis v tag cid c k a ns _) = do -gen20ChainPayloads :: IO () -gen20ChainPayloads = traverse_ mk20ChainPayload [developmentKAD, mainnetKAD] - where - mk20ChainPayload (Genesis v tag cid c k a ns) = do +-- ((ccAr,ccCode,_,_),_) <- mkApiReq coinContractV2 +-- v2Install <- TIO.readFile coinContractV2Install +-- let ccCode' = ccCode <> v2Install +-- ccAr' = ccAr +-- { _ylCode = Just ccCode' +-- , _ylCodeFile = Nothing +-- , _ylNonce = Just "coin-contract-v2-temp" +-- } +-- (_,ccTx) <- mkApiReqCmd False coinContractV2 ccAr' - ((ccAr,ccCode,_,_),_) <- mkApiReq coinContractV2 - v2Install <- TIO.readFile coinContractV2Install - let ccCode' = ccCode <> v2Install - ccAr' = ccAr - { _ylCode = Just ccCode' - , _ylCodeFile = Nothing - , _ylNonce = Just "coin-contract-v2-temp" - } - (_,ccTx) <- mkApiReqCmd False coinContractV2 ccAr' +-- fa1 <- mkTx fungibleAssetV1 +-- fa2 <- mkTx fungibleAssetV2 +-- gp <- mkTx gasPayer - fa1 <- mkTx fungibleAssetV1 - fa2 <- mkTx fungibleAssetV2 - gp <- mkTx gasPayer +-- txs <- ([fa1,fa2,ccTx,gp] ++) <$> +-- mapM mkTx (toList ns <> toList k <> toList a <> toList c) +-- cwTxs <- mkChainwebTxs' txs - txs <- ([fa1,fa2,ccTx,gp] ++) <$> - mapM mkTx (toList ns <> toList k <> toList a <> toList c) - cwTxs <- mkChainwebTxs' txs - - printf ("Generating Genesis 20-chain payload for %s on " <> show_ cid <> "...\n") $ show v - genPayloadModule' v (tag <> sshow cid) cwTxs +-- printf ("Generating Genesis 20-chain payload for %s on " <> show_ cid <> "...\n") $ show v +-- genPayloadModule v (tag <> T.pack (chainIdRangeTag cid)) cid cwTxs genCoinV3Payloads :: IO () genCoinV3Payloads = genTxModule "CoinV3" [coinContractV3] @@ -175,11 +188,8 @@ genCoinV5Payloads = genTxModule "CoinV5" -- Payload Generation --------------------- -genPayloadModule :: ChainwebVersion -> Text -> [FilePath] -> IO () -genPayloadModule v tag txFiles = genPayloadModule' v tag =<< mkChainwebTxs txFiles - -genPayloadModule' :: ChainwebVersion -> Text -> [ChainwebTransaction] -> IO () -genPayloadModule' v tag cwTxs = +genPayloadModule :: ChainwebVersion -> Text -> ChainId -> [ChainwebTransaction] -> IO Text +genPayloadModule v tag cid cwTxs = withTempRocksDb "chainweb-ea" $ \rocks -> withBlockHeaderDb rocks v cid $ \bhdb -> do let logger = genericLogger Warn TIO.putStrLn @@ -189,20 +199,18 @@ genPayloadModule' v tag cwTxs = withPactService v cid logger bhdb pdb env defaultPactServiceConfig $ execNewGenesisBlock noMiner (V.fromList cwTxs) - let payloadYaml = TE.decodeUtf8 $ Yaml.encode payloadWO + let + payloadYaml = TE.decodeUtf8 $ Yaml.encode payloadWO -- Encode yaml as list of Haskell string literals. The extra empty line -- at the end is for backward compatibility. payloadHaskell = " [ " - <> (T.intercalate "\n , " $ quoted <$> (T.lines payloadYaml <> [""])) + <> T.intercalate "\n , " (quoted <$> (T.lines payloadYaml <> [""])) <> "\n ]" - modl = T.unlines $ startModule tag <> [payloadHaskell] - fileName = "src/Chainweb/BlockHeader/Genesis/" <> tag <> "Payload.hs" - - TIO.writeFile (T.unpack fileName) modl + return $! T.unlines $ startModule tag <> [payloadHaskell] + -- cid = someChainId v where - cid = someChainId v quoted t = "\"" <> t <> "\"" @@ -233,14 +241,14 @@ mkChainwebTxs txFiles = mkChainwebTxs' =<< traverse mkTx txFiles mkChainwebTxs' :: [Command Text] -> IO [ChainwebTransaction] mkChainwebTxs' rawTxs = do - forM rawTxs $ \cmd -> do - let cmdBS = fmap TE.encodeUtf8 cmd - procCmd = verifyCommand cmdBS - case procCmd of - f@ProcFail{} -> fail (show f) - ProcSucc c -> do - let t = toTxCreationTime (Time (TimeSpan 0)) - return $! mkPayloadWithTextOld <$> (c & setTxTime t & setTTL (TTLSeconds $ 2 * 24 * 60 * 60)) + forM rawTxs $ \cmd -> do + let cmdBS = fmap TE.encodeUtf8 cmd + procCmd = verifyCommand cmdBS + case procCmd of + f@ProcFail{} -> fail (show f) + ProcSucc c -> do + let t = toTxCreationTime (Time (TimeSpan 0)) + return $! mkPayloadWithTextOld <$> (c & setTxTime t & setTTL (TTLSeconds $ 2 * 24 * 60 * 60)) where setTxTime = set (cmdPayload . pMeta . pmCreationTime) setTTL = set (cmdPayload . pMeta . pmTTL) @@ -275,16 +283,17 @@ genTxModules = void $ do genTxModule :: Text -> [FilePath] -> IO () genTxModule tag txFiles = do - putStrLn $ "Generating tx module for " ++ show tag - cwTxs <- mkChainwebTxs txFiles + putStrLn $ "Generating tx module for " ++ show tag + cwTxs <- mkChainwebTxs txFiles - let encTxs = map quoteTx cwTxs - quoteTx tx = " \"" <> encTx tx <> "\"" - encTx = encodeB64UrlNoPaddingText . codecEncode (chainwebPayloadCodec maxBound) - modl = T.unlines $ startTxModule tag <> [T.intercalate "\n ,\n" encTxs] <> endTxModule - fileName = "src/Chainweb/Pact/Transactions/" <> tag <> "Transactions.hs" + let + encTxs = map quoteTx cwTxs + quoteTx tx = " \"" <> encTx tx <> "\"" + encTx = encodeB64UrlNoPaddingText . codecEncode (chainwebPayloadCodec maxBound) + modl = T.unlines $ startTxModule tag <> [T.intercalate "\n ,\n" encTxs] <> endTxModule + fileName = "src/Chainweb/Pact/Transactions/" <> tag <> "Transactions.hs" - TIO.writeFile (T.unpack fileName) modl + TIO.writeFile (T.unpack fileName) modl startTxModule :: Text -> [Text] startTxModule tag = diff --git a/tools/ea/Ea/Genesis.hs b/tools/ea/Ea/Genesis.hs index 341bb3e1cd..b8f8a7ca7c 100644 --- a/tools/ea/Ea/Genesis.hs +++ b/tools/ea/Ea/Genesis.hs @@ -1,17 +1,21 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} module Ea.Genesis ( -- * Genesis tx data Genesis(..) -, GChainId(..) +, ChainIdRange(..) +, chainIdRangeTag -- * Devnet Genesis Txs , development0 , developmentN , developmentKAD +, fastDevelopment0 +, fastDevelopmentN - -- * Devnet (testing) Genesis Txs + -- * Testing Genesis Txs , fastTimedCPM0 , fastTimedCPMN @@ -49,11 +53,13 @@ module Ea.Genesis import Control.Lens import Data.Text +import Data.Word import Chainweb.Graph import Chainweb.Test.TestVersions import Chainweb.Version import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Mainnet import Chainweb.Version.Testnet @@ -61,75 +67,52 @@ import Chainweb.Version.Testnet -- ---------------------------------------------------------------------- -- -- Genesis Tx Data --- | Type-safe representation of chain ids --- for a 10-chain graph. --- -data GChainId - = Zero - | One - | Two - | Three - | Four - | Five - | Six - | Seven - | Eight - | Nine - | N - | KAD - deriving (Eq, Ord, Enum) - -instance Show GChainId where - show = \case - Zero -> "0" - One -> "1" - Two -> "2" - Three -> "3" - Four -> "4" - Five -> "5" - Six -> "6" - Seven -> "7" - Eight -> "8" - Nine -> "9" - N -> "N" - KAD -> "KAD" +-- A range of chain IDs [l,r] +data ChainIdRange + = ChainIdRange !Word32 !Word32 + deriving (Eq, Ord) + +chainIdRangeTag :: ChainIdRange -> String +chainIdRangeTag (ChainIdRange l u) + | l == u = show l + | otherwise = show l <> "to" <> show u + +-- instance Show GChainId where +-- show = \case +-- Zero -> "0" +-- One -> "1" +-- Two -> "2" +-- Three -> "3" +-- Four -> "4" +-- Five -> "5" +-- Six -> "6" +-- Seven -> "7" +-- Eight -> "8" +-- Nine -> "9" +-- N -> "N" +-- KAD -> "KAD" -- | Genesis transaction record -- data Genesis = Genesis - { _version :: ChainwebVersion - -- ^ chainweb version (e.g. Testnet04) - , _tag :: Text - -- ^ Module name tag - , _txChainId :: GChainId - -- ^ chain id - , _coinbase :: Maybe FilePath - -- ^ filepath to coinbase yaml - , _keysets :: Maybe FilePath - -- ^ filepath to keyset yaml - , _allocations :: Maybe FilePath - -- ^ filepath to allocation yaml - , _namespaces :: Maybe FilePath - -- ^ filepath to namespace yaml - } deriving (Eq, Ord, Show) - --- | A 'Lens'' into the transaction chain id --- of a genesis payload --- -txChainId :: Lens' Genesis GChainId -txChainId = lens _txChainId (\t b -> t { _txChainId = b }) - --- | A 'Lens'' into the transaction allocation filepath --- of a genesis payload --- -allocations :: Lens' Genesis (Maybe FilePath) -allocations = lens _allocations (\t b -> t { _allocations = b }) - --- | A 'Lens'' into the transaction allocation filepath --- of a genesis payload --- -coinbase :: Lens' Genesis (Maybe FilePath) -coinbase = lens _coinbase (\t b -> t { _coinbase = b }) + { _version :: ChainwebVersion + -- ^ chainweb version (e.g. Testnet04) + , _tag :: Text + -- ^ Module name tag + , _txChainIds :: ChainIdRange + -- ^ chain id + , _coinbase :: Maybe FilePath + -- ^ filepath to coinbase yaml + , _keysets :: Maybe FilePath + -- ^ filepath to keyset yaml + , _allocations :: Maybe FilePath + -- ^ filepath to allocation yaml + , _namespaces :: Maybe FilePath + -- ^ filepath to namespace yaml + , _coinContract :: [FilePath] + } deriving (Eq, Ord) -- Show) + +makeLenses ''Genesis -- ---------------------------------------------------------------------- -- -- Coin Contract Essentials @@ -147,7 +130,7 @@ coinContractV2 :: FilePath coinContractV2 = "pact/coin-contract/v2/load-coin-contract-v2.yaml" coinContractV2Install :: FilePath -coinContractV2Install = "pact/coin-contract/v2/coin-install.pact" +coinContractV2Install = "pact/coin-contract/v2/install-coin-contract-v2.yaml" coinContractV3 :: FilePath coinContractV3 = "pact/coin-contract/v3/load-coin-contract-v3.yaml" @@ -158,6 +141,9 @@ coinContractV4 = "pact/coin-contract/v4/load-coin-contract-v4.yaml" coinContractV5 :: FilePath coinContractV5 = "pact/coin-contract/v5/load-coin-contract-v5.yaml" +installCoinContractV5 :: FilePath +installCoinContractV5 = "pact/coin-contract/v5/install-coin-contract-v5.yaml" + fungibleAssetV2 :: FilePath fungibleAssetV2 = "pact/coin-contract/v2/load-fungible-asset-v2.yaml" @@ -171,29 +157,45 @@ development0 :: Genesis development0 = Genesis { _version = Development , _tag = "Development" - , _txChainId = Zero + , _txChainIds = ChainIdRange 0 0 , _coinbase = Just dev0Grants , _keysets = Just devKeysets , _allocations = Just devAllocations , _namespaces = Just devNs + , _coinContract = [fungibleAssetV1, coinContractV1, gasPayer] } developmentN :: Genesis developmentN = development0 - & txChainId .~ N - & coinbase .~ (Just devNGrants) + & txChainIds .~ ChainIdRange 1 9 + & coinbase .~ Just devNGrants developmentKAD :: Genesis -developmentKAD = Genesis - { _version = Development - , _tag = "Development" - , _txChainId = KAD - , _coinbase = Just devnetKadOps - , _keysets = Nothing - , _allocations = Nothing +developmentKAD = development0 + & txChainIds .~ ChainIdRange 10 19 + & coinbase .~ Just devnetKadOps + & keysets .~ Nothing + & allocations .~ Nothing + & namespaces .~ Just devNs + & coinContract .~ [fungibleAssetV1, fungibleAssetV2, coinContractV2Install, gasPayer] + +fastDevelopment0 :: Genesis +fastDevelopment0 = Genesis + { _version = FastDevelopment + , _tag = "FastDevelopment" + , _txChainIds = ChainIdRange 0 0 + , _coinbase = Just dev0Grants + , _keysets = Just devKeysets + , _allocations = Just devAllocations , _namespaces = Just devNs + , _coinContract = [fungibleAssetV1, fungibleXChainV1, fungibleAssetV2, installCoinContractV5, gasPayer] } +fastDevelopmentN :: Genesis +fastDevelopmentN = fastDevelopment0 + & txChainIds .~ ChainIdRange 1 19 + & coinbase .~ (Just devNGrants) + devNs :: FilePath devNs = "pact/genesis/ns.yaml" @@ -219,16 +221,17 @@ fastTimedCPM0 :: Genesis fastTimedCPM0 = Genesis { _version = fastForkingCpmTestVersion petersonChainGraph , _tag = "FastTimedCPM" - , _txChainId = Zero + , _txChainIds = ChainIdRange 0 0 , _coinbase = Just fast0Grants , _keysets = Just fastKeysets , _allocations = Just fastAllocations , _namespaces = Just fastNs + , _coinContract = [fungibleAssetV1, coinContractV1, gasPayer] } fastTimedCPMN :: Genesis fastTimedCPMN = fastTimedCPM0 - & txChainId .~ N + & txChainIds .~ ChainIdRange 1 9 & coinbase .~ (Just fastNGrants) fastNs :: FilePath @@ -253,16 +256,17 @@ testnet0 :: Genesis testnet0 = Genesis { _version = Testnet04 , _tag = "Testnet" - , _txChainId = Zero + , _txChainIds = ChainIdRange 0 0 , _coinbase = Just test0Grants , _keysets = Just testnetKeysets , _allocations = Just testnetAllocations , _namespaces = Just testNs + , _coinContract = [fungibleAssetV1, coinContractV1, gasPayer] } testnetN :: Genesis testnetN = testnet0 - & txChainId .~ N + & txChainIds .~ ChainIdRange 1 19 & coinbase .~ (Just testNGrants) test0Grants :: FilePath @@ -287,67 +291,69 @@ mainnet0 :: Genesis mainnet0 = Genesis { _version = Mainnet01 , _tag = "Mainnet" - , _txChainId = Zero + , _txChainIds = ChainIdRange 0 0 , _coinbase = Nothing , _keysets = Just mainnetKeysets , _allocations = Just mainnetAllocations0 , _namespaces = Just mainNs + , _coinContract = [fungibleAssetV1, coinContractV1, gasPayer] } mainnet1 :: Genesis mainnet1 = mainnet0 - & txChainId .~ One + & txChainIds .~ ChainIdRange 1 1 & allocations .~ (Just mainnetAllocations1) mainnet2 :: Genesis mainnet2 = mainnet0 - & txChainId .~ Two + & txChainIds .~ ChainIdRange 2 2 & allocations .~ (Just mainnetAllocations2) mainnet3 :: Genesis mainnet3 = mainnet0 - & txChainId .~ Three + & txChainIds .~ ChainIdRange 3 3 & allocations .~ (Just mainnetAllocations3) mainnet4 :: Genesis mainnet4 = mainnet0 - & txChainId .~ Four + & txChainIds .~ ChainIdRange 4 4 & allocations .~ (Just mainnetAllocations4) mainnet5 :: Genesis mainnet5 = mainnet0 - & txChainId .~ Five + & txChainIds .~ ChainIdRange 5 5 & allocations .~ (Just mainnetAllocations5) mainnet6 :: Genesis mainnet6 = mainnet0 - & txChainId .~ Six + & txChainIds .~ ChainIdRange 6 6 & allocations .~ (Just mainnetAllocations6) mainnet7 :: Genesis mainnet7 = mainnet0 - & txChainId .~ Seven + & txChainIds .~ ChainIdRange 7 7 & allocations .~ (Just mainnetAllocations7) mainnet8 :: Genesis mainnet8 = mainnet0 - & txChainId .~ Eight + & txChainIds .~ ChainIdRange 8 8 & allocations .~ (Just mainnetAllocations8) mainnet9 :: Genesis mainnet9 = mainnet0 - & txChainId .~ Nine + & txChainIds .~ ChainIdRange 9 9 & allocations .~ (Just mainnetAllocations9) mainnetKAD :: Genesis mainnetKAD = Genesis { _version = Mainnet01 , _tag = "Mainnet" - , _txChainId = KAD + , _txChainIds = ChainIdRange 10 19 , _coinbase = Just mainnetKadOps , _keysets = Nothing , _allocations = Nothing , _namespaces = Just mainNs + , _coinContract = [fungibleAssetV1, fungibleAssetV2, coinContractV2Install, gasPayer] } mainnetKadOps :: FilePath From 4376642b894da916055f963b47b4b98205201d0a Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 11 Apr 2023 21:19:04 -0400 Subject: [PATCH 52/91] SPV PoC with creation and verification --- bench/Bench.hs | 1 + src/Chainweb/Pact/RestAPI/Server.hs | 15 +++++-- src/Chainweb/Pact/SPV.hs | 7 +++- src/Chainweb/SPV.hs | 57 ++++++++++++++++++-------- src/Chainweb/SPV/CreateProof.hs | 40 ++++++++++++++---- src/Chainweb/SPV/VerifyProof.hs | 18 +++++--- test/Chainweb/Test/Orphans/Internal.hs | 6 +++ test/Chainweb/Test/Roundtrips.hs | 1 + 8 files changed, 107 insertions(+), 38 deletions(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index 98c2f88f8f..8e1208fa8f 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -16,6 +16,7 @@ import qualified JSONEncoding import Chainweb.Storage.Table.RocksDB import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Registry main :: IO () diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index d6ae595860..5def725702 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -8,6 +8,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -419,16 +420,22 @@ spvHandler l cdb cid (SpvRequest rk (Pact.ChainId ptid)) = do <> sshow e Right i -> return i - tid <- chainIdFromText ptid - p <- liftIO (try $ createTransactionOutputProof cdb tid cid bhe idx) >>= \case + p <- if + | Just netID <- T.stripPrefix "crossnet:" ptid -> + liftIO $ try $ createCrossNetworkTransactionOutputProof cdb netID cid bhe idx + | Just tid <- chainIdFromText ptid -> + liftIO $ try $ createTransactionOutputProof cdb tid cid bhe idx + | otherwise -> + toErr "Invalid proof target: not a chain ID or crossnet:" + + case p of Left e@SpvExceptionTargetNotReachable{} -> toErr $ "SPV target not reachable: " <> spvErrOf e Left e@SpvExceptionVerificationFailed{} -> toErr $ "SPV verification failed: " <> spvErrOf e Left e -> toErr $ "Internal error: SPV verification failed: " <> spvErrOf e - Right q -> return q + Right q -> return $! b64 q - return $! b64 p where pe = _webPactExecutionService $ view CutDB.cutDbPactService cdb ph = Pact.fromUntypedHash $ unRequestKey rk diff --git a/src/Chainweb/Pact/SPV.hs b/src/Chainweb/Pact/SPV.hs index 3f716ba3b8..a3e275b514 100644 --- a/src/Chainweb/Pact/SPV.hs +++ b/src/Chainweb/Pact/SPV.hs @@ -129,8 +129,11 @@ verifySPV bdb bh typ proof = go typ proof "TXOUT" -> case extractProof enableBridge o of Left t -> return (Left t) Right u - | (view outputProofChainId u) /= cid -> + | ProofTargetChain tcid <- view outputProofTarget u, tcid /= cid -> internalError "cannot redeem spv proof on wrong target chain" + -- NOT allowed to use ProofTargetCrossNetwork yet + | ProofTargetCrossNetwork _ <- view outputProofTarget u -> + internalError "cannot use verify-spv with a cross-network proof" | otherwise -> do -- SPV proof verification is a 3 step process: @@ -173,7 +176,7 @@ verifyCont bdb bh (ContProof cp) = do case decodeStrict' t of Nothing -> internalError "unable to decode continuation proof" Just u - | (view outputProofChainId u) /= cid -> + | ProofTargetChain tcid <- view outputProofTarget u, tcid /= cid -> internalError "cannot redeem continuation proof on wrong target chain" | otherwise -> do diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index ffb0f4327e..6425ec52b9 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -1,11 +1,14 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TypeFamilies #-} -- | @@ -19,10 +22,11 @@ -- module Chainweb.SPV ( SpvException(..) +, ProofTarget(..) , TransactionProof(..) , proofChainId , TransactionOutputProof(..) -, outputProofChainId +, outputProofTarget ) where import Control.Applicative @@ -37,6 +41,7 @@ import Data.Aeson import qualified Data.Aeson.Types as Aeson import qualified Data.ByteString as B import Data.MerkleLog hiding (Expected, Actual) +import Data.Text (Text) import qualified Data.Text as T import GHC.Generics (Generic) @@ -60,7 +65,7 @@ data SpvException { _spvExceptionMsg :: !T.Text , _spvExceptionSourceChainId :: !ChainId , _spvExceptionSourceHeight :: !BlockHeight - , _spvExceptionTargetChainId :: !ChainId + , _spvExceptionTargetChainId :: !ProofTarget , _spvExceptionTargetHeight :: !BlockHeight } | SpvExceptionInconsistentPayloadData @@ -90,11 +95,11 @@ instance Exception SpvException proofProperties :: forall kv . KeyValue kv - => ChainId + => ProofTarget -> MerkleProof SHA512t_256 -> [kv] -proofProperties cid p = - [ "chain" .= cid +proofProperties tgt p = + [ "chain" .= tgt , "object" .= obj (_merkleProofObject p) , "subject" .= JsonProofSubject (_getMerkleProofSubject $ _merkleProofSubject p) , "algorithm" .= ("SHA512t_256" :: T.Text) @@ -123,14 +128,19 @@ instance ToJSON JsonProofSubject where parseProof :: String - -> (ChainId -> MerkleProof SHA512t_256 -> a) + -> (ProofTarget -> MerkleProof SHA512t_256 -> Aeson.Parser a) -> Value -> Aeson.Parser a -parseProof name mkProof = withObject name $ \o -> mkProof - <$> o .: "chain" +parseProof name mkProof = withObject name $ \o -> join $ mkProof + <$> target o <*> parse o <* (assertJSON ("SHA512t_256" :: T.Text) =<< o .: "algorithm") where + target o = + o .: "chain" >>= \case + (T.stripPrefix "crossnet:" -> Just tgt) -> return $ ProofTargetCrossNetwork tgt + (chainIdFromText -> Just cid) -> return $ ProofTargetChain cid + _ -> fail "expected numeric chain ID or crossnet:" parse o = MerkleProof <$> (parseSubject =<< o .: "subject") <*> (parseObject =<< o .: "object") @@ -169,13 +179,13 @@ data TransactionProof a = TransactionProof deriving (Show, Eq) instance ToJSON (TransactionProof SHA512t_256) where - toJSON (TransactionProof cid p) = object $ proofProperties cid p - toEncoding (TransactionProof cid p) = pairs . mconcat $ proofProperties cid p + toJSON (TransactionProof cid p) = object $ proofProperties (ProofTargetChain cid) p + toEncoding (TransactionProof cid p) = pairs . mconcat $ proofProperties (ProofTargetChain cid) p {-# INLINE toJSON #-} {-# INLINE toEncoding #-} instance FromJSON (TransactionProof SHA512t_256) where - parseJSON = parseProof "TransactionProof" TransactionProof + parseJSON = parseProof "TransactionProof" (\tgt p -> do { ProofTargetChain cid <- return tgt; return (TransactionProof cid p) }) {-# INLINE parseJSON #-} -- | Getter into the chain id of a 'TransactionProof' @@ -185,12 +195,25 @@ proofChainId = to (\(TransactionProof cid _) -> cid) -- -------------------------------------------------------------------------- -- -- Output Proofs +-- +-- | Targets for proofs, usually a chain ID, but in the case of L2, a network +-- and chain identifier. TODO: determine format, if any. +data ProofTarget = ProofTargetChain !ChainId | ProofTargetCrossNetwork !Text + deriving stock (Show, Eq, Ord, Generic) + deriving anyclass NFData + +instance ToJSON ProofTarget where + toJSON (ProofTargetChain cid) = toJSON cid + toJSON (ProofTargetCrossNetwork subtgt) = toJSON ("crossnet:" <> subtgt) + toEncoding (ProofTargetChain cid) = toEncoding cid + toEncoding (ProofTargetCrossNetwork subtgt) = toEncoding ("crossnet:" <> subtgt) + -- | Witness that a transaction output is included in the head of a chain in a -- chainweb. -- data TransactionOutputProof a = TransactionOutputProof - !ChainId + !ProofTarget -- ^ the target chain of the proof, i.e the chain which contains -- the root of the proof. !(MerkleProof a) @@ -199,16 +222,16 @@ data TransactionOutputProof a = TransactionOutputProof deriving (Show, Eq) instance ToJSON (TransactionOutputProof SHA512t_256) where - toJSON (TransactionOutputProof cid p) = object $ proofProperties cid p - toEncoding (TransactionOutputProof cid p) = pairs . mconcat $ proofProperties cid p + toJSON (TransactionOutputProof tgt p) = object $ proofProperties tgt p + toEncoding (TransactionOutputProof tgt p) = pairs . mconcat $ proofProperties tgt p {-# INLINE toJSON #-} {-# INLINE toEncoding #-} instance FromJSON (TransactionOutputProof SHA512t_256) where - parseJSON = parseProof "TransactionOutputProof" TransactionOutputProof + parseJSON = parseProof "TransactionOutputProof" (\tgt p -> return (TransactionOutputProof tgt p)) {-# INLINE parseJSON #-} -- | Getter into the chain id of a 'TransactionOutputProof' -- -outputProofChainId :: Getter (TransactionOutputProof a) ChainId -outputProofChainId = to (\(TransactionOutputProof cid _) -> cid) +outputProofTarget :: Getter (TransactionOutputProof a) ProofTarget +outputProofTarget = to (\(TransactionOutputProof tgt _) -> tgt) diff --git a/src/Chainweb/SPV/CreateProof.hs b/src/Chainweb/SPV/CreateProof.hs index 424d570d4b..6ed475c041 100644 --- a/src/Chainweb/SPV/CreateProof.hs +++ b/src/Chainweb/SPV/CreateProof.hs @@ -22,6 +22,7 @@ module Chainweb.SPV.CreateProof , createTransactionOutputProof , createTransactionOutputProof_ , createTransactionOutputProof' +, createCrossNetworkTransactionOutputProof ) where import Control.Applicative @@ -34,6 +35,7 @@ import Crypto.Hash.Algorithms import qualified Data.ByteString as B import qualified Data.List.NonEmpty as N import Data.MerkleLog +import Data.Text (Text) import GHC.Stack @@ -188,7 +190,6 @@ createTransactionOutputProof cutDb = (view cutDbWebBlockHeaderDb cutDb) (view cutDbPayloadDb cutDb) - -- | Version without CutDb dependency -- createTransactionOutputProof_ @@ -209,7 +210,7 @@ createTransactionOutputProof_ -> IO (TransactionOutputProof SHA512t_256) createTransactionOutputProof_ headerDb payloadDb tcid scid bh i = do trgHeader <- minimumTrgHeader headerDb tcid scid bh - TransactionOutputProof tcid + TransactionOutputProof (ProofTargetChain tcid) <$> createPayloadProof_ outputProofPrefix headerDb payloadDb tcid scid bh i trgHeader @@ -234,7 +235,7 @@ createTransactionOutputProof' -- ^ The index of the transaction in the block -> IO (TransactionOutputProof SHA512t_256) createTransactionOutputProof' cutDb tcid scid bh i - = TransactionOutputProof tcid + = TransactionOutputProof (ProofTargetChain tcid) <$> createPayloadProof outputProofPrefix cutDb tcid scid bh i outputProofPrefix @@ -248,7 +249,7 @@ outputProofPrefix i db payload = do -- 1. TX proof Just outs <- tableLookup blockOutputTable $ _blockPayloadOutputsHash payload -- TODO: use the transaction tree cache - let (!subj, pos, t) = bodyTree @_ @ChainwebHashTag outs i + let !(!subj, pos, t) = bodyTree @_ @ChainwebHashTag outs i -- FIXME use log let tree = (pos, t) -- we blindly trust the ix @@ -259,6 +260,27 @@ outputProofPrefix i db payload = do where blockOutputTable = _payloadCacheBlockOutputs $ _payloadCache db +-- TODO: incorporate version into crossnet: syntax? +-- TODO: if and when this is less trivial, incorporate this logic into createPayloadProof +createCrossNetworkTransactionOutputProof :: (CanReadablePayloadCas tbl, HasCallStack) => CutDb tbl -> Text -> ChainId -> BlockHeight -> Int -> IO (TransactionOutputProof SHA512t_256) +createCrossNetworkTransactionOutputProof cutDb tgt scid txHeight txIx = do + tip <- maxEntry headerDb + txHeader <- maybe (throwM headerNotFound) pure =<< seekAncestor headerDb tip (int txHeight) + Just payload <- tableLookup pDb (_blockPayloadHash txHeader) + (subj, prefix) <- outputProofPrefix txIx payloadDb payload + TransactionOutputProof (ProofTargetCrossNetwork tgt) <$> merkleProof_ subj prefix + where + headerDb = cutDb ^?! cutDbWebBlockHeaderDb . ixg scid + payloadDb = view cutDbPayloadDb cutDb + pDb = _transactionDbBlockPayloads $ _transactionDb payloadDb + headerNotFound = SpvExceptionTargetNotReachable + { _spvExceptionMsg = "Target of SPV proof can't be reached from the source transaction" + , _spvExceptionSourceChainId = scid + , _spvExceptionSourceHeight = txHeight + , _spvExceptionTargetChainId = ProofTargetCrossNetwork tgt + , _spvExceptionTargetHeight = txHeight + } + -- -------------------------------------------------------------------------- -- -- Internal Proof Creation @@ -342,7 +364,7 @@ createPayloadProof_ getPrefix headerDb payloadDb tcid scid txHeight txIx trgHead { _spvExceptionMsg = "target chain not reachable. Chainweb instance is too young" , _spvExceptionSourceChainId = scid , _spvExceptionSourceHeight = txHeight - , _spvExceptionTargetChainId = tcid + , _spvExceptionTargetChainId = ProofTargetChain tcid , _spvExceptionTargetHeight = _blockHeight trgHeader } @@ -351,7 +373,7 @@ createPayloadProof_ getPrefix headerDb payloadDb tcid scid txHeight txIx trgHead { _spvExceptionMsg = "Target of SPV proof can't be reached from the source transaction" , _spvExceptionSourceChainId = scid , _spvExceptionSourceHeight = txHeight - , _spvExceptionTargetChainId = tcid + , _spvExceptionTargetChainId = ProofTargetChain tcid , _spvExceptionTargetHeight = _blockHeight trgHeader } @@ -362,7 +384,7 @@ createPayloadProof_ getPrefix headerDb payloadDb tcid scid txHeight txIx trgHead { _spvExceptionMsg = "Target of SPV proof can't be reached from the source transaction" , _spvExceptionSourceChainId = scid , _spvExceptionSourceHeight = txHeight - , _spvExceptionTargetChainId = tcid + , _spvExceptionTargetChainId = ProofTargetChain tcid , _spvExceptionTargetHeight = _blockHeight trgHeader } @@ -430,7 +452,7 @@ crumbsOnChain db trgHeader srcHeight go p (cur : acc) -- | Create a path of bread crumbs from the source chain id to the target header --- along the adjancet parent relation. +-- along the adjacent parent relation. -- -- Returns 'Nothing' if no such path exists. -- @@ -481,7 +503,7 @@ minimumTrgHeader headerDb tcid scid bh = do { _spvExceptionMsg = "target chain not reachable. Chainweb instance is too young" , _spvExceptionSourceChainId = scid , _spvExceptionSourceHeight = bh - , _spvExceptionTargetChainId = tcid + , _spvExceptionTargetChainId = ProofTargetChain tcid , _spvExceptionTargetHeight = int trgHeight } where diff --git a/src/Chainweb/SPV/VerifyProof.hs b/src/Chainweb/SPV/VerifyProof.hs index ee5d3de238..39ae2977a3 100644 --- a/src/Chainweb/SPV/VerifyProof.hs +++ b/src/Chainweb/SPV/VerifyProof.hs @@ -126,9 +126,12 @@ verifyTransactionOutputProof :: CutDb tbl -> TransactionOutputProof SHA512t_256 -> IO TransactionOutput -verifyTransactionOutputProof cutDb proof@(TransactionOutputProof cid p) = do - unlessM (member cutDb cid h) $ throwM - $ SpvExceptionVerificationFailed "target header is not in the chain" +verifyTransactionOutputProof cutDb proof@(TransactionOutputProof tgt p) = do + case tgt of + ProofTargetChain cid -> + unlessM (member cutDb cid h) $ throwM + $ SpvExceptionVerificationFailed "target header is not in the chain" + ProofTargetCrossNetwork _net -> return () proofSubject p where h = runTransactionOutputProof proof @@ -145,9 +148,12 @@ verifyTransactionOutputProofAt -> TransactionOutputProof SHA512t_256 -> BlockHash -> IO TransactionOutput -verifyTransactionOutputProofAt cutDb proof@(TransactionOutputProof cid p) ctx = do - unlessM (memberOfM cutDb cid h ctx) $ throwM - $ SpvExceptionVerificationFailed "target header is not in the chain" +verifyTransactionOutputProofAt cutDb proof@(TransactionOutputProof tgt p) ctx = do + case tgt of + ProofTargetChain cid -> + unlessM (memberOfM cutDb cid h ctx) $ throwM + $ SpvExceptionVerificationFailed "target header is not in the chain" + ProofTargetCrossNetwork _net -> return () proofSubject p where h = runTransactionOutputProof proof diff --git a/test/Chainweb/Test/Orphans/Internal.hs b/test/Chainweb/Test/Orphans/Internal.hs index bbb660397c..18f621e0eb 100644 --- a/test/Chainweb/Test/Orphans/Internal.hs +++ b/test/Chainweb/Test/Orphans/Internal.hs @@ -887,6 +887,12 @@ instance Arbitrary Spv2Request where instance Arbitrary (TransactionProof ChainwebMerkleHashAlgorithm) where arbitrary = TransactionProof <$> arbitrary <*> arbitrary +instance Arbitrary ProofTarget where + arbitrary = oneof + [ ProofTargetChain <$> arbitrary + , ProofTargetCrossNetwork <$> arbitrary + ] + instance Arbitrary (TransactionOutputProof ChainwebMerkleHashAlgorithm) where arbitrary = TransactionOutputProof <$> arbitrary <*> arbitrary diff --git a/test/Chainweb/Test/Roundtrips.hs b/test/Chainweb/Test/Roundtrips.hs index 9ae9727a84..574588551e 100644 --- a/test/Chainweb/Test/Roundtrips.hs +++ b/test/Chainweb/Test/Roundtrips.hs @@ -280,6 +280,7 @@ jsonTestCases f = , testProperty "SpvRequest" $ f @SpvRequest , testProperty "Spv2Request" $ f @Spv2Request , testProperty "TransactionProof" $ f @(TransactionProof ChainwebMerkleHashAlgorithm) + , testProperty "ProofTarget" $ f @ProofTarget , testProperty "TransactionOutputProof" $ f @(TransactionOutputProof ChainwebMerkleHashAlgorithm) , testProperty "PayloadProof" $ f @(PayloadProof ChainwebMerkleHashAlgorithm) , testProperty "SomePayloadProof" $ f @(SomePayloadProof) From a66c3d68c508acd646d05ddef2d2fe6d6e2fc890 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 11 Apr 2023 21:45:16 -0400 Subject: [PATCH 53/91] Fix tests --- src/Chainweb/SPV.hs | 13 +++++++------ tools/cwtool/TxSimulator.hs | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index 6425ec52b9..ef6efa0399 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -132,15 +132,10 @@ parseProof -> Value -> Aeson.Parser a parseProof name mkProof = withObject name $ \o -> join $ mkProof - <$> target o + <$> o .: "chain" <*> parse o <* (assertJSON ("SHA512t_256" :: T.Text) =<< o .: "algorithm") where - target o = - o .: "chain" >>= \case - (T.stripPrefix "crossnet:" -> Just tgt) -> return $ ProofTargetCrossNetwork tgt - (chainIdFromText -> Just cid) -> return $ ProofTargetChain cid - _ -> fail "expected numeric chain ID or crossnet:" parse o = MerkleProof <$> (parseSubject =<< o .: "subject") <*> (parseObject =<< o .: "object") @@ -208,6 +203,12 @@ instance ToJSON ProofTarget where toEncoding (ProofTargetChain cid) = toEncoding cid toEncoding (ProofTargetCrossNetwork subtgt) = toEncoding ("crossnet:" <> subtgt) +instance FromJSON ProofTarget where + parseJSON = withText "ProofTarget" $ \case + (T.stripPrefix "crossnet:" -> Just tgt) -> return $ ProofTargetCrossNetwork tgt + (chainIdFromText -> Just cid) -> return $ ProofTargetChain cid + _ -> fail "expected numeric chain ID or crossnet:" + -- | Witness that a transaction output is included in the head of a chain in a -- chainweb. diff --git a/tools/cwtool/TxSimulator.hs b/tools/cwtool/TxSimulator.hs index 255ca85ba8..af113e3dc8 100644 --- a/tools/cwtool/TxSimulator.hs +++ b/tools/cwtool/TxSimulator.hs @@ -170,7 +170,8 @@ spvSim sc bh pwo = do case decodeStrict' t of Nothing -> internalError "unable to decode continuation proof" Just (TransactionOutputProof pcid p :: TransactionOutputProof SHA512t_256) -> do - unless (pcid == scChain sc) $ + -- TODO: crossnetwork pacts + unless (pcid == ProofTargetChain (scChain sc)) $ internalError "cannot redeem continuation proof on wrong target chain" TransactionOutput tout <- proofSubject p case decodeStrict' tout :: Maybe (CommandResult Hash) of From 44ae9c849e83649493425d094bc42d1d20ded3e8 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Tue, 11 Apr 2023 21:25:46 -0400 Subject: [PATCH 54/91] fix build --- bench/Bench.hs | 1 + tools/ea/Ea.hs | 5 ++--- tools/ea/Ea/Genesis.hs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bench/Bench.hs b/bench/Bench.hs index 98c2f88f8f..8e1208fa8f 100644 --- a/bench/Bench.hs +++ b/bench/Bench.hs @@ -16,6 +16,7 @@ import qualified JSONEncoding import Chainweb.Storage.Table.RocksDB import Chainweb.Version.Development +import Chainweb.Version.FastDevelopment import Chainweb.Version.Registry main :: IO () diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index e25e9bae8b..911f80392a 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -62,7 +62,6 @@ import Chainweb.Transaction (ChainwebTransaction, chainwebPayloadCodec, mkPayloadWithTextOld) import Chainweb.Utils import Chainweb.Version -import Chainweb.Version.Utils (someChainId) import Chainweb.Storage.Table.RocksDB @@ -125,14 +124,14 @@ mkPayloads :: Traversable t => t Genesis -> IO () mkPayloads = traverse_ (\g -> writePayload g =<< mkPayload g) writePayload :: Genesis -> Text -> IO () -writePayload gen@(Genesis v tag cid c k a ns cc) payload = do +writePayload gen payload = do let fileName = "src/Chainweb/BlockHeader/Genesis/" <> fullGenesisTag gen <> "Payload.hs" TIO.writeFile (T.unpack fileName) payload -- | Generate a payload for a given list of genesis transactions -- mkPayload :: Genesis -> IO Text -mkPayload gen@(Genesis v tag cidr@(ChainIdRange l u) c k a ns cc) = do +mkPayload gen@(Genesis v _ cidr@(ChainIdRange l u) c k a ns cc) = do printf ("Generating Genesis Payload for %s on " <> show_ cidr <> "...\n") $ show v payloadModules <- for [l..u] $ \cid -> genPayloadModule v (fullGenesisTag gen) (unsafeChainId cid) =<< mkChainwebTxs txs diff --git a/tools/ea/Ea/Genesis.hs b/tools/ea/Ea/Genesis.hs index b8f8a7ca7c..1dd6b8e3c3 100644 --- a/tools/ea/Ea/Genesis.hs +++ b/tools/ea/Ea/Genesis.hs @@ -112,7 +112,7 @@ data Genesis = Genesis , _coinContract :: [FilePath] } deriving (Eq, Ord) -- Show) -makeLenses ''Genesis +makeLensesFor [("_" <> fn, fn) | fn <- ["txChainIds", "coinbase", "keysets", "allocations", "namespaces", "coinContract"]] ''Genesis -- ---------------------------------------------------------------------- -- -- Coin Contract Essentials From c85a9930d1792f274c0678278a64163efaa558e4 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 12 Apr 2023 11:34:57 -0400 Subject: [PATCH 55/91] Move upgrades to after 20 chain move --- src/Chainweb/Version/Development.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 0a4a9cdc5f..412ba9366f 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -53,17 +53,17 @@ devnet = ChainwebVersion OldDAGuard -> AllChains $ ForkAtBlockHeight 1 PactEvents -> AllChains $ ForkAtBlockHeight 1 SPVBridge -> AllChains $ ForkAtBlockHeight 1 - Pact4Coin3 -> AllChains $ ForkAtBlockHeight 2 + Pact4Coin3 -> AllChains $ ForkAtBlockHeight 13 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight 1 Pact420 -> AllChains $ ForkAtBlockHeight 1 CheckTxHash -> AllChains $ ForkAtBlockHeight 1 - Chainweb213Pact -> AllChains $ ForkAtBlockHeight 3 - Chainweb214Pact -> AllChains $ ForkAtBlockHeight 4 - Chainweb215Pact -> AllChains $ ForkAtBlockHeight 5 + Chainweb213Pact -> AllChains $ ForkAtBlockHeight 13 + Chainweb214Pact -> AllChains $ ForkAtBlockHeight 14 + Chainweb215Pact -> AllChains $ ForkAtBlockHeight 15 Pact44NewTrans -> AllChains $ ForkAtBlockHeight 1 - Chainweb216Pact -> AllChains $ ForkAtBlockHeight 6 - Chainweb217Pact -> AllChains $ ForkAtBlockHeight 6 - Chainweb218Pact -> AllChains $ ForkAtBlockHeight 6 + Chainweb216Pact -> AllChains $ ForkAtBlockHeight 16 + Chainweb217Pact -> AllChains $ ForkAtBlockHeight 16 + Chainweb218Pact -> AllChains $ ForkAtBlockHeight 16 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ forkUpgrades devnet From 63c09220234560b5b9da34ceddf25c66b35457ea Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 12 Apr 2023 11:34:57 -0400 Subject: [PATCH 56/91] Move upgrades to after 20 chain move --- src/Chainweb/Version/Development.hs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 0a4a9cdc5f..89707266bb 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -53,17 +53,17 @@ devnet = ChainwebVersion OldDAGuard -> AllChains $ ForkAtBlockHeight 1 PactEvents -> AllChains $ ForkAtBlockHeight 1 SPVBridge -> AllChains $ ForkAtBlockHeight 1 - Pact4Coin3 -> AllChains $ ForkAtBlockHeight 2 + Pact4Coin3 -> AllChains $ ForkAtBlockHeight 14 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight 1 Pact420 -> AllChains $ ForkAtBlockHeight 1 CheckTxHash -> AllChains $ ForkAtBlockHeight 1 - Chainweb213Pact -> AllChains $ ForkAtBlockHeight 3 - Chainweb214Pact -> AllChains $ ForkAtBlockHeight 4 - Chainweb215Pact -> AllChains $ ForkAtBlockHeight 5 + Chainweb213Pact -> AllChains $ ForkAtBlockHeight 15 + Chainweb214Pact -> AllChains $ ForkAtBlockHeight 15 + Chainweb215Pact -> AllChains $ ForkAtBlockHeight 16 Pact44NewTrans -> AllChains $ ForkAtBlockHeight 1 - Chainweb216Pact -> AllChains $ ForkAtBlockHeight 6 - Chainweb217Pact -> AllChains $ ForkAtBlockHeight 6 - Chainweb218Pact -> AllChains $ ForkAtBlockHeight 6 + Chainweb216Pact -> AllChains $ ForkAtBlockHeight 17 + Chainweb217Pact -> AllChains $ ForkAtBlockHeight 17 + Chainweb218Pact -> AllChains $ ForkAtBlockHeight 17 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ forkUpgrades devnet From 28897e018d092eb2db6a79d9ab8388677d28bd3a Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 13 Apr 2023 14:10:00 -0400 Subject: [PATCH 57/91] NSv2 in fast devnet --- pact/genesis/{ns.yaml => ns-v1.yaml} | 0 pact/genesis/ns-v2.yaml | 9 ++ pact/namespaces/ns-install.pact | 114 ++++++++++++++++++ .../Genesis/Development10to19Payload.hs | 8 +- .../Genesis/FastDevelopment0Payload.hs | 10 +- .../Genesis/FastDevelopment1to19Payload.hs | 10 +- tools/ea/Ea/Genesis.hs | 11 +- 7 files changed, 144 insertions(+), 18 deletions(-) rename pact/genesis/{ns.yaml => ns-v1.yaml} (100%) create mode 100644 pact/genesis/ns-v2.yaml create mode 100644 pact/namespaces/ns-install.pact diff --git a/pact/genesis/ns.yaml b/pact/genesis/ns-v1.yaml similarity index 100% rename from pact/genesis/ns.yaml rename to pact/genesis/ns-v1.yaml diff --git a/pact/genesis/ns-v2.yaml b/pact/genesis/ns-v2.yaml new file mode 100644 index 0000000000..f722229c15 --- /dev/null +++ b/pact/genesis/ns-v2.yaml @@ -0,0 +1,9 @@ +codeFile: ../namespaces/ns-install.pact +data: + ns-admin-keyset: ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] + ns-operate-keyset: ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] + ns-genesis-keyset: + keys: [] + pred: "=" +nonce: load-ns-devnet-sender00 +keyPairs: [] diff --git a/pact/namespaces/ns-install.pact b/pact/namespaces/ns-install.pact new file mode 100644 index 0000000000..a5cfa2f582 --- /dev/null +++ b/pact/namespaces/ns-install.pact @@ -0,0 +1,114 @@ +(define-keyset 'ns-admin-keyset (read-keyset 'ns-admin-keyset)) +(define-keyset 'ns-operate-keyset (read-keyset 'ns-genesis-keyset)) + +(module ns GOVERNANCE + "Administers definition of new namespaces in Chainweb." + + (defschema reg-entry + admin-guard:guard + active:bool) + + (deftable registry:{reg-entry}) + + (defcap GOVERNANCE () + (enforce-keyset 'ns-admin-keyset)) + + (defcap OPERATE () + (enforce-keyset 'ns-operate-keyset)) + + (defconst GUARD_SUCCESS (create-user-guard (success))) + (defconst GUARD_FAILURE (create-user-guard (failure))) + + (defun success () + true) + (defun failure () + (enforce false "Disabled")) + + (defun validate-name (name) + (enforce (!= "" name) "Empty name not allowed") + (enforce (< (length name) 64) "Name must be less than 64 characters long") + (enforce (is-charset CHARSET_LATIN1 name) + "Name must be in latin1 charset")) + + (defun create-principal-namespace:string + ( g:guard + ) + " Format principal namespace as Pact hash (BLAKE2b256) of principal \ + \ in hex truncated to 160 bits (40 characters), prepended with 'n_'.\ + \ Only w: and k: account protocols are supported. " + + (let + ((ty (typeof-principal (create-principal g)))) + + ;; only w: and k: currently supported + (if (or (= ty "k:") (= ty "w:")) + (+ "n_" (take 40 (int-to-str 16 (str-to-int 64 (hash g))))) + (enforce false + (format "Unsupported guard protocol: {}" [ty])) + )) + ) + + (defun validate:bool + ( ns-name:string + ns-admin:guard + ) + " Manages namespace install for Chainweb. \ + \ Allows principal namespaces. \ + \ Non-principal namespaces require active row in registry \ + \ for NS-NAME with guard matching NS-ADMIN." + + (if (= (create-principal-namespace ns-admin) ns-name) + + true ;; allow principal namespaces + + (with-default-read registry ns-name ;; otherwise enforce registry + { 'admin-guard : ns-admin + , 'active : false } + { 'admin-guard := ag + , 'active := is-active } + + (enforce is-active "Inactive or unregistered namespace") + (enforce (= ns-admin ag) "Admin guard must match guard in registry") + + true) + )) + + (defun write-registry:string + ( ns-name:string + guard:guard + active:bool + ) + " Write entry with GUARD and ACTIVE into registry for NAME. \ + \ Guarded by operate keyset. " + + (with-capability (OPERATE) + + (validate-name ns-name) + + (write registry ns-name + { 'admin-guard: guard + , 'active: active }) + + "Register entry written")) + + (defun query:object{reg-entry} + ( ns-name:string ) + (read registry ns-name)) + +) + +(create-table registry) + +(write-registry "kadena" + (keyset-ref-guard 'ns-operate-keyset) true) +(write-registry "user" GUARD_FAILURE true) +(write-registry "free" GUARD_FAILURE true) + +(define-namespace "kadena" + (keyset-ref-guard 'ns-operate-keyset) + (keyset-ref-guard 'ns-operate-keyset)) + +(define-namespace "user" GUARD_SUCCESS GUARD_FAILURE) +(define-namespace "free" GUARD_SUCCESS GUARD_FAILURE) +;;rotate to real operate keyset +(define-keyset 'ns-operate-keyset (read-keyset 'ns-operate-keyset)) diff --git a/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs b/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs index af17ff18bf..67817d115d 100644 --- a/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs +++ b/src/Chainweb/BlockHeader/Genesis/Development10to19Payload.hs @@ -19,17 +19,17 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , "- - eyJoYXNoIjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZnVuZ2libGUtdjJcXG5cXG4gIFxcXCIgU3RhbmRhcmQgZm9yIGZ1bmdpYmxlIGNvaW5zIGFuZCB0b2tlbnMgYXMgc3BlY2lmaWVkIGluIEtJUC0wMDAyLiBcXFwiXFxuXFxuICAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICAgOyBTY2hlbWFcXG5cXG4gICAoZGVmc2NoZW1hIGFjY291bnQtZGV0YWlsc1xcbiAgICBAZG9jIFxcXCJTY2hlbWEgZm9yIHJlc3VsdHMgb2YgJ2FjY291bnQnIG9wZXJhdGlvbi5cXFwiXFxuICAgIEBtb2RlbCBbIChpbnZhcmlhbnQgKCE9IFxcXCJcXFwiIHNlbmRlcikpIF1cXG5cXG4gICAgYWNjb3VudDpzdHJpbmdcXG4gICAgYmFsYW5jZTpkZWNpbWFsXFxuICAgIGd1YXJkOmd1YXJkKVxcblxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgQ2Fwc1xcblxcbiAgIChkZWZjYXAgVFJBTlNGRVI6Ym9vbFxcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZWQgY2FwYWJpbGl0eSBzZWFsaW5nIEFNT1VOVCBmb3IgdHJhbnNmZXIgZnJvbSBTRU5ERVIgdG8gXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLiBQZXJtaXRzIGFueSBudW1iZXIgb2YgdHJhbnNmZXJzIHVwIHRvIEFNT1VOVC5cXFwiXFxuICAgICBAbWFuYWdlZCBhbW91bnQgVFJBTlNGRVItbWdyXFxuICAgICApXFxuXFxuICAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICAoIG1hbmFnZWQ6ZGVjaW1hbFxcbiAgICAgICByZXF1ZXN0ZWQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIE1hbmFnZXMgVFJBTlNGRVIgQU1PVU5UIGxpbmVhcmx5LCBcXFxcXFxuICAgICAgICAgIFxcXFwgc3VjaCB0aGF0IGEgcmVxdWVzdCBmb3IgMS4wIGFtb3VudCBvbiBhIDMuMCBcXFxcXFxuICAgICAgICAgIFxcXFwgbWFuYWdlZCBxdWFudGl0eSBlbWl0cyB1cGRhdGVkIGFtb3VudCAyLjAuXFxcIlxcbiAgICAgKVxcblxcbiAgIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgIDsgRnVuY3Rpb25hbGl0eVxcblxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2MgXFxcIiBUcmFuc2ZlciBBTU9VTlQgYmV0d2VlbiBhY2NvdW50cyBTRU5ERVIgYW5kIFJFQ0VJVkVSLiBcXFxcXFxuICAgICAgICAgXFxcXCBGYWlscyBpZiBlaXRoZXIgU0VOREVSIG9yIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LlxcXCJcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoIT0gcmVjZWl2ZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICBdXFxuICAgIClcXG5cXG4gICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAgKCBzZW5kZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICAgKVxcbiAgICAgQGRvYyBcXFwiIFRyYW5zZmVyIEFNT1VOVCBiZXR3ZWVuIGFjY291bnRzIFNFTkRFUiBhbmQgUkVDRUlWRVIuIFxcXFxcXG4gICAgICAgICAgXFxcXCBGYWlscyBpZiBTRU5ERVIgZG9lcyBub3QgZXhpc3QuIElmIFJFQ0VJVkVSIGV4aXN0cywgZ3VhcmQgXFxcXFxcbiAgICAgICAgICBcXFxcIG11c3QgbWF0Y2ggZXhpc3RpbmcgdmFsdWUuIElmIFJFQ0VJVkVSIGRvZXMgbm90IGV4aXN0LCBcXFxcXFxuICAgICAgICAgIFxcXFwgUkVDRUlWRVIgYWNjb3VudCBpcyBjcmVhdGVkIHVzaW5nIFJFQ0VJVkVSLUdVQVJELiBcXFxcXFxuICAgICAgICAgIFxcXFwgU3ViamVjdCB0byBtYW5hZ2VtZW50IGJ5IFRSQU5TRkVSIGNhcGFiaWxpdHkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICAgcmVjZWl2ZXI6c3RyaW5nXFxuICAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgIHRhcmdldC1jaGFpbjpzdHJpbmdcXG4gICAgICAgYW1vdW50OmRlY2ltYWxcXG4gICAgIClcXG4gICAgIEBkb2MgXFxcIiAyLXN0ZXAgcGFjdCB0byB0cmFuc2ZlciBBTU9VTlQgZnJvbSBTRU5ERVIgb24gY3VycmVudCBjaGFpbiBcXFxcXFxuICAgICAgICAgIFxcXFwgdG8gUkVDRUlWRVIgb24gVEFSR0VULUNIQUlOIHZpYSBTUFYgcHJvb2YuIFxcXFxcXG4gICAgICAgICAgXFxcXCBUQVJHRVQtQ0hBSU4gbXVzdCBiZSBkaWZmZXJlbnQgdGhhbiBjdXJyZW50IGNoYWluIGlkLiBcXFxcXFxuICAgICAgICAgIFxcXFwgRmlyc3Qgc3RlcCBkZWJpdHMgQU1PVU5UIGNvaW5zIGluIFNFTkRFUiBhY2NvdW50IGFuZCB5aWVsZHMgXFxcXFxcbiAgICAgICAgICBcXFxcIFJFQ0VJVkVSLCBSRUNFSVZFUl9HVUFSRCBhbmQgQU1PVU5UIHRvIFRBUkdFVC1DSEFJTi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFNlY29uZCBzdGVwIGNvbnRpbnVhdGlvbiBpcyBzZW50IGludG8gVEFSR0VULUNIQUlOIHdpdGggcHJvb2YgXFxcXFxcbiAgICAgICAgICBcXFxcIG9idGFpbmVkIGZyb20gdGhlIHNwdiAnb3V0cHV0JyBlbmRwb2ludCBvZiBDaGFpbndlYi4gXFxcXFxcbiAgICAgICAgICBcXFxcIFByb29mIGlzIHZhbGlkYXRlZCBhbmQgUkVDRUlWRVIgaXMgY3JlZGl0ZWQgd2l0aCBBTU9VTlQgXFxcXFxcbiAgICAgICAgICBcXFxcIGNyZWF0aW5nIGFjY291bnQgd2l0aCBSRUNFSVZFUl9HVUFSRCBhcyBuZWNlc3NhcnkuXFxcIlxcbiAgICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgXFxcIlxcXCIpKVxcbiAgICAgICAgICAgICAgKHByb3BlcnR5ICghPSByZWNlaXZlciBcXFwiXFxcIikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHNlbmRlciByZWNlaXZlcikpXFxuICAgICAgICAgICAgICAocHJvcGVydHkgKCE9IHRhcmdldC1jaGFpbiBcXFwiXFxcIikpXFxuICAgICAgICAgICAgXVxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBnZXQtYmFsYW5jZTpkZWNpbWFsXFxuICAgICAoIGFjY291bnQ6c3RyaW5nIClcXG4gICAgIFxcXCIgR2V0IGJhbGFuY2UgZm9yIEFDQ09VTlQuIEZhaWxzIGlmIGFjY291bnQgZG9lcyBub3QgZXhpc3QuXFxcIlxcbiAgICAgKVxcblxcbiAgIChkZWZ1biBkZXRhaWxzOm9iamVjdHthY2NvdW50LWRldGFpbHN9XFxuICAgICAoIGFjY291bnQ6IHN0cmluZyApXFxuICAgICBcXFwiIEdldCBhbiBvYmplY3Qgd2l0aCBkZXRhaWxzIG9mIEFDQ09VTlQuIFxcXFxcXG4gICAgIFxcXFwgRmFpbHMgaWYgYWNjb3VudCBkb2VzIG5vdCBleGlzdC5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICAoKVxcbiAgICAgXFxcIlJldHVybiB0aGUgbWF4aW11bSBhbGxvd2VkIGRlY2ltYWwgcHJlY2lzaW9uLlxcXCJcXG4gICAgIClcXG5cXG4gICAoZGVmdW4gZW5mb3JjZS11bml0OmJvb2xcXG4gICAgICggYW1vdW50OmRlY2ltYWwgKVxcbiAgICAgXFxcIiBFbmZvcmNlIG1pbmltdW0gcHJlY2lzaW9uIGFsbG93ZWQgZm9yIHRyYW5zYWN0aW9ucy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIGNyZWF0ZS1hY2NvdW50OnN0cmluZ1xcbiAgICAgKCBhY2NvdW50OnN0cmluZ1xcbiAgICAgICBndWFyZDpndWFyZFxcbiAgICAgKVxcbiAgICAgXFxcIiBDcmVhdGUgQUNDT1VOVCB3aXRoIDAuMCBiYWxhbmNlLCB3aXRoIEdVQVJEIGNvbnRyb2xsaW5nIGFjY2Vzcy5cXFwiXFxuICAgICApXFxuXFxuICAgKGRlZnVuIHJvdGF0ZTpzdHJpbmdcXG4gICAgICggYWNjb3VudDpzdHJpbmdcXG4gICAgICAgbmV3LWd1YXJkOmd1YXJkXFxuICAgICApXFxuICAgICBcXFwiIFJvdGF0ZSBndWFyZCBmb3IgQUNDT1VOVC4gVHJhbnNhY3Rpb24gaXMgdmFsaWRhdGVkIGFnYWluc3QgXFxcXFxcbiAgICAgXFxcXCBleGlzdGluZyBndWFyZCBiZWZvcmUgaW5zdGFsbGluZyBuZXcgZ3VhcmQuIFxcXCJcXG4gICAgIClcXG5cXG4pXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJmdW5naWJsZS1hc3NldC12MlwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZnVuZ2libGUtdjIifSwicmVxS2V5IjoiMDVCdGo3ZUJaQlc3by1TYUxvVmhBaWNNVVBaVUJiRzZRVDhfTEFrQ3hIcyIsImxvZ3MiOiJWaGlrLVYzOHByQXRpbHhTV3RZNWYxUnpmVjFUYVJBQzF0N3VVVXZjbGxnIiwibWV0YURhdGEiOm51bGwsImNvbnRpbnVhdGlvbiI6bnVsbCwidHhJZCI6MX0" , "- - eyJoYXNoIjoieEpIYXlrdmFIMDlQWXpOcVNBTF9FWDlrNU40c2MybUMtdmU2TzlPSTQyWSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihtb2R1bGUgY29pbiBHT1ZFUk5BTkNFXFxuXFxuICBAZG9jIFxcXCInY29pbicgcmVwcmVzZW50cyB0aGUgS2FkZW5hIENvaW4gQ29udHJhY3QuIFRoaXMgY29udHJhY3QgcHJvdmlkZXMgYm90aCB0aGUgXFxcXFxcbiAgXFxcXGJ1eS9yZWRlZW0gZ2FzIHN1cHBvcnQgaW4gdGhlIGZvcm0gb2YgJ2Z1bmQtdHgnLCBhcyB3ZWxsIGFzIHRyYW5zZmVyLCAgICAgICBcXFxcXFxuICBcXFxcY3JlZGl0LCBkZWJpdCwgY29pbmJhc2UsIGFjY291bnQgY3JlYXRpb24gYW5kIHF1ZXJ5LCBhcyB3ZWxsIGFzIFNQViBidXJuICAgIFxcXFxcXG4gIFxcXFxjcmVhdGUuIFRvIGFjY2VzcyB0aGUgY29pbiBjb250cmFjdCwgeW91IG1heSB1c2UgaXRzIGZ1bGx5LXF1YWxpZmllZCBuYW1lLCAgXFxcXFxcbiAgXFxcXG9yIGlzc3VlIHRoZSAnKHVzZSBjb2luKScgY29tbWFuZCBpbiB0aGUgYm9keSBvZiBhIG1vZHVsZSBkZWNsYXJhdGlvbi5cXFwiXFxuXFxuICBAbW9kZWxcXG4gICAgWyAoZGVmcHJvcGVydHkgY29uc2VydmVzLW1hc3NcXG4gICAgICAgICg9IChjb2x1bW4tZGVsdGEgY29pbi10YWJsZSAnYmFsYW5jZSkgMC4wKSlcXG5cXG4gICAgICAoZGVmcHJvcGVydHkgdmFsaWQtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgICAgICAoYW5kXFxuICAgICAgICAgICg-PSAobGVuZ3RoIGFjY291bnQpIDMpXFxuICAgICAgICAgICg8PSAobGVuZ3RoIGFjY291bnQpIDI1NikpKVxcbiAgICBdXFxuXFxuICAoaW1wbGVtZW50cyBmdW5naWJsZS12MilcXG5cXG4gIDsgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cXG4gIDsgU2NoZW1hcyBhbmQgVGFibGVzXFxuXFxuICAoZGVmc2NoZW1hIGNvaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlRoZSBjb2luIGNvbnRyYWN0IHRva2VuIHNjaGVtYVxcXCJcXG4gICAgQG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBndWFyZDpndWFyZClcXG5cXG4gIChkZWZ0YWJsZSBjb2luLXRhYmxlOntjb2luLXNjaGVtYX0pXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENhcGFiaWxpdGllc1xcblxcbiAgKGRlZmNhcCBHT1ZFUk5BTkNFICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJFbmZvcmNlIG5vbi11cGdyYWRlYWJpbGl0eVxcXCIpKVxcblxcbiAgKGRlZmNhcCBHQVMgKClcXG4gICAgXFxcIk1hZ2ljIGNhcGFiaWxpdHkgdG8gcHJvdGVjdCBnYXMgYnV5IGFuZCByZWRlZW1cXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIENPSU5CQVNFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IHRvIHByb3RlY3QgbWluZXIgcmV3YXJkXFxcIlxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBHRU5FU0lTICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGNvbnN0cmFpbmluZyBnZW5lc2lzIHRyYW5zYWN0aW9uc1xcXCJcXG4gICAgdHJ1ZSlcXG5cXG4gIChkZWZjYXAgUkVNRURJQVRFICgpXFxuICAgIFxcXCJNYWdpYyBjYXBhYmlsaXR5IGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnNcXFwiXFxuICAgIHRydWUpXFxuXFxuICAoZGVmY2FwIERFQklUIChzZW5kZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgZGViaXRpbmcgb3BlcmF0aW9uc1xcXCJcXG4gICAgKGVuZm9yY2UtZ3VhcmQgKGF0ICdndWFyZCAocmVhZCBjb2luLXRhYmxlIHNlbmRlcikpKVxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIFxcXCJcXFwiKSBcXFwidmFsaWQgc2VuZGVyXFxcIikpXFxuXFxuICAoZGVmY2FwIENSRURJVCAocmVjZWl2ZXI6c3RyaW5nKVxcbiAgICBcXFwiQ2FwYWJpbGl0eSBmb3IgbWFuYWdpbmcgY3JlZGl0aW5nIG9wZXJhdGlvbnNcXFwiXFxuICAgIChlbmZvcmNlICghPSByZWNlaXZlciBcXFwiXFxcIikgXFxcInZhbGlkIHJlY2VpdmVyXFxcIikpXFxuXFxuICAoZGVmY2FwIFJPVEFURSAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkF1dG9ub21vdXNseSBtYW5hZ2VkIGNhcGFiaWxpdHkgZm9yIGd1YXJkIHJvdGF0aW9uXFxcIlxcbiAgICBAbWFuYWdlZFxcbiAgICB0cnVlKVxcblxcbiAgKGRlZmNhcCBUUkFOU0ZFUjpib29sXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsXFxuICAgIClcXG4gICAgQG1hbmFnZWQgYW1vdW50IFRSQU5TRkVSLW1nclxcbiAgICAoZW5mb3JjZSAoIT0gc2VuZGVyIHJlY2VpdmVyKSBcXFwic2FtZSBzZW5kZXIgYW5kIHJlY2VpdmVyXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApIFxcXCJQb3NpdGl2ZSBhbW91bnRcXFwiKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpKVxcbiAgICAoY29tcG9zZS1jYXBhYmlsaXR5IChDUkVESVQgcmVjZWl2ZXIpKVxcbiAgKVxcblxcbiAgKGRlZnVuIFRSQU5TRkVSLW1ncjpkZWNpbWFsXFxuICAgICggbWFuYWdlZDpkZWNpbWFsXFxuICAgICAgcmVxdWVzdGVkOmRlY2ltYWxcXG4gICAgKVxcblxcbiAgICAobGV0ICgobmV3YmFsICgtIG1hbmFnZWQgcmVxdWVzdGVkKSkpXFxuICAgICAgKGVuZm9yY2UgKD49IG5ld2JhbCAwLjApXFxuICAgICAgICAoZm9ybWF0IFxcXCJUUkFOU0ZFUiBleGNlZWRlZCBmb3IgYmFsYW5jZSB7fVxcXCIgW21hbmFnZWRdKSlcXG4gICAgICBuZXdiYWwpXFxuICApXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvbnN0YW50c1xcblxcbiAgKGRlZmNvbnN0IENPSU5fQ0hBUlNFVCBDSEFSU0VUX0xBVElOMVxcbiAgICBcXFwiVGhlIGRlZmF1bHQgY29pbiBjb250cmFjdCBjaGFyYWN0ZXIgc2V0XFxcIilcXG5cXG4gIChkZWZjb25zdCBNSU5JTVVNX1BSRUNJU0lPTiAxMlxcbiAgICBcXFwiTWluaW11bSBhbGxvd2VkIHByZWNpc2lvbiBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiKVxcblxcbiAgKGRlZmNvbnN0IE1JTklNVU1fQUNDT1VOVF9MRU5HVEggM1xcbiAgICBcXFwiTWluaW11bSBhY2NvdW50IGxlbmd0aCBhZG1pc3NpYmxlIGZvciBjb2luIGFjY291bnRzXFxcIilcXG5cXG4gIChkZWZjb25zdCBNQVhJTVVNX0FDQ09VTlRfTEVOR1RIIDI1NlxcbiAgICBcXFwiTWF4aW11bSBhY2NvdW50IG5hbWUgbGVuZ3RoIGFkbWlzc2libGUgZm9yIGNvaW4gYWNjb3VudHNcXFwiKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBVdGlsaXRpZXNcXG5cXG4gIChkZWZ1biBlbmZvcmNlLXVuaXQ6Ym9vbCAoYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgbWluaW11bSBwcmVjaXNpb24gYWxsb3dlZCBmb3IgY29pbiB0cmFuc2FjdGlvbnNcXFwiXFxuXFxuICAgIChlbmZvcmNlXFxuICAgICAgKD0gKGZsb29yIGFtb3VudCBNSU5JTVVNX1BSRUNJU0lPTilcXG4gICAgICAgICBhbW91bnQpXFxuICAgICAgKGZvcm1hdCBcXFwiQW1vdW50IHZpb2xhdGVzIG1pbmltdW0gcHJlY2lzaW9uOiB7fVxcXCIgW2Ftb3VudF0pKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdmFsaWRhdGUtYWNjb3VudCAoYWNjb3VudDpzdHJpbmcpXFxuICAgIEBkb2MgXFxcIkVuZm9yY2UgdGhhdCBhbiBhY2NvdW50IG5hbWUgY29uZm9ybXMgdG8gdGhlIGNvaW4gY29udHJhY3QgXFxcXFxcbiAgICAgICAgIFxcXFxtaW5pbXVtIGFuZCBtYXhpbXVtIGxlbmd0aCByZXF1aXJlbWVudHMsIGFzIHdlbGwgYXMgdGhlICAgIFxcXFxcXG4gICAgICAgICBcXFxcbGF0aW4tMSBjaGFyYWN0ZXIgc2V0LlxcXCJcXG5cXG4gICAgKGVuZm9yY2VcXG4gICAgICAoaXMtY2hhcnNldCBDT0lOX0NIQVJTRVQgYWNjb3VudClcXG4gICAgICAoZm9ybWF0XFxuICAgICAgICBcXFwiQWNjb3VudCBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBjb2luIGNvbnRyYWN0IGNoYXJzZXQ6IHt9XFxcIlxcbiAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgKGxldCAoKGFjY291bnQtbGVuZ3RoIChsZW5ndGggYWNjb3VudCkpKVxcblxcbiAgICAgIChlbmZvcmNlXFxuICAgICAgICAoPj0gYWNjb3VudC1sZW5ndGggTUlOSU1VTV9BQ0NPVU5UX0xFTkdUSClcXG4gICAgICAgIChmb3JtYXRcXG4gICAgICAgICAgXFxcIkFjY291bnQgbmFtZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRoZSBtaW4gbGVuZ3RoIHJlcXVpcmVtZW50OiB7fVxcXCJcXG4gICAgICAgICAgW2FjY291bnRdKSlcXG5cXG4gICAgICAoZW5mb3JjZVxcbiAgICAgICAgKDw9IGFjY291bnQtbGVuZ3RoIE1BWElNVU1fQUNDT1VOVF9MRU5HVEgpXFxuICAgICAgICAoZm9ybWF0XFxuICAgICAgICAgIFxcXCJBY2NvdW50IG5hbWUgZG9lcyBub3QgY29uZm9ybSB0byB0aGUgbWF4IGxlbmd0aCByZXF1aXJlbWVudDoge31cXFwiXFxuICAgICAgICAgIFthY2NvdW50XSkpXFxuICAgICAgKVxcbiAgKVxcblxcbiAgOyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxcbiAgOyBDb2luIENvbnRyYWN0XFxuXFxuICAoZGVmdW4gZ2FzLW9ubHkgKClcXG4gICAgXFxcIlByZWRpY2F0ZSBmb3IgZ2FzLW9ubHkgdXNlciBndWFyZHMuXFxcIlxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKSlcXG5cXG4gIChkZWZ1biBnYXMtZ3VhcmQgKGd1YXJkOmd1YXJkKVxcbiAgICBcXFwiUHJlZGljYXRlIGZvciBnYXMgKyBzaW5nbGUga2V5IHVzZXIgZ3VhcmRzXFxcIlxcbiAgICAoZW5mb3JjZS1vbmVcXG4gICAgICBcXFwiRW5mb3JjZSBlaXRoZXIgdGhlIHByZXNlbmNlIG9mIGEgR0FTIGNhcCBvciBrZXlzZXRcXFwiXFxuICAgICAgWyAoZ2FzLW9ubHkpXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG4gICAgICBdKSlcXG5cXG4gIChkZWZ1biBidXktZ2FzOnN0cmluZyAoc2VuZGVyOnN0cmluZyB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCJUaGlzIGZ1bmN0aW9uIGRlc2NyaWJlcyB0aGUgbWFpbiAnZ2FzIGJ1eScgb3BlcmF0aW9uLiBBdCB0aGlzIHBvaW50IFxcXFxcXG4gICAgXFxcXE1JTkVSIGhhcyBiZWVuIGNob3NlbiBmcm9tIHRoZSBwb29sLCBhbmQgd2lsbCBiZSB2YWxpZGF0ZWQuIFRoZSBTRU5ERVIgICBcXFxcXFxuICAgIFxcXFxvZiB0aGlzIHRyYW5zYWN0aW9uIGhhcyBzcGVjaWZpZWQgYSBnYXMgbGltaXQgTElNSVQgKG1heGltdW0gZ2FzKSBmb3IgICAgXFxcXFxcbiAgICBcXFxcdGhlIHRyYW5zYWN0aW9uLCBhbmQgdGhlIHByaWNlIGlzIHRoZSBzcG90IHByaWNlIG9mIGdhcyBhdCB0aGF0IHRpbWUuICAgIFxcXFxcXG4gICAgXFxcXFRoZSBnYXMgYnV5IHdpbGwgYmUgZXhlY3V0ZWQgcHJpb3IgdG8gZXhlY3V0aW5nIFNFTkRFUidzIGNvZGUuXFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCB0b3RhbClcXG4gICAgKGVuZm9yY2UgKD4gdG90YWwgMC4wKSBcXFwiZ2FzIHN1cHBseSBtdXN0IGJlIGEgcG9zaXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHQVMpKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuICAgICAgKGRlYml0IHNlbmRlciB0b3RhbCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZWRlZW0tZ2FzOnN0cmluZyAobWluZXI6c3RyaW5nIG1pbmVyLWd1YXJkOmd1YXJkIHNlbmRlcjpzdHJpbmcgdG90YWw6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiVGhpcyBmdW5jdGlvbiBkZXNjcmliZXMgdGhlIG1haW4gJ3JlZGVlbSBnYXMnIG9wZXJhdGlvbi4gQXQgdGhpcyAgICBcXFxcXFxuICAgIFxcXFxwb2ludCwgdGhlIFNFTkRFUidzIHRyYW5zYWN0aW9uIGhhcyBiZWVuIGV4ZWN1dGVkLCBhbmQgdGhlIGdhcyB0aGF0ICAgICAgXFxcXFxcbiAgICBcXFxcd2FzIGNoYXJnZWQgaGFzIGJlZW4gY2FsY3VsYXRlZC4gTUlORVIgd2lsbCBiZSBjcmVkaXRlZCB0aGUgZ2FzIGNvc3QsICAgIFxcXFxcXG4gICAgXFxcXGFuZCBTRU5ERVIgd2lsbCByZWNlaXZlIHRoZSByZW1haW5kZXIgdXAgdG8gdGhlIGxpbWl0XFxcIlxcblxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKD4gdG90YWwgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHNlbmRlcikpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBtaW5lcikpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHNlbmRlcilcXG4gICAgKHZhbGlkYXRlLWFjY291bnQgbWluZXIpXFxuICAgIChlbmZvcmNlLXVuaXQgdG90YWwpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKEdBUykpXFxuICAgIChsZXQqXFxuICAgICAgKChmZWUgKHJlYWQtZGVjaW1hbCBcXFwiZmVlXFxcIikpXFxuICAgICAgIChyZWZ1bmQgKC0gdG90YWwgZmVlKSkpXFxuXFxuICAgICAgKGVuZm9yY2UtdW5pdCBmZWUpXFxuICAgICAgKGVuZm9yY2UgKD49IGZlZSAwLjApXFxuICAgICAgICBcXFwiZmVlIG11c3QgYmUgYSBub24tbmVnYXRpdmUgcXVhbnRpdHlcXFwiKVxcblxcbiAgICAgIChlbmZvcmNlICg-PSByZWZ1bmQgMC4wKVxcbiAgICAgICAgXFxcInJlZnVuZCBtdXN0IGJlIGEgbm9uLW5lZ2F0aXZlIHF1YW50aXR5XFxcIilcXG5cXG4gICAgICAgIDsgZGlyZWN0bHkgdXBkYXRlIGluc3RlYWQgb2YgY3JlZGl0XFxuICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHNlbmRlcilcXG4gICAgICAgIChpZiAoPiByZWZ1bmQgMC4wKVxcbiAgICAgICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgc2VuZGVyXFxuICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOj0gYmFsYW5jZSB9XFxuICAgICAgICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIHNlbmRlclxcbiAgICAgICAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCI6ICgrIGJhbGFuY2UgcmVmdW5kKSB9KSlcXG5cXG4gICAgICAgICAgXFxcIm5vb3BcXFwiKSlcXG5cXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChDUkVESVQgbWluZXIpXFxuICAgICAgICAoaWYgKD4gZmVlIDAuMClcXG4gICAgICAgICAgKGNyZWRpdCBtaW5lciBtaW5lci1ndWFyZCBmZWUpXFxuICAgICAgICAgIFxcXCJub29wXFxcIikpXFxuICAgICAgKVxcblxcbiAgICApXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFjY291bnQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZClcXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKSBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChpbnNlcnQgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgICA6IGd1YXJkXFxuICAgICAgfSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGdldC1iYWxhbmNlOmRlY2ltYWwgKGFjY291bnQ6c3RyaW5nKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcbiAgICAgIGJhbGFuY2VcXG4gICAgICApXFxuICAgIClcXG5cXG4gIChkZWZ1biBkZXRhaWxzOm9iamVjdHtmdW5naWJsZS12Mi5hY2NvdW50LWRldGFpbHN9XFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIiA6PSBnIH1cXG4gICAgICB7IFxcXCJhY2NvdW50XFxcIiA6IGFjY291bnRcXG4gICAgICAsIFxcXCJiYWxhbmNlXFxcIiA6IGJhbFxcbiAgICAgICwgXFxcImd1YXJkXFxcIjogZyB9KVxcbiAgICApXFxuXFxuICAoZGVmdW4gcm90YXRlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgbmV3LWd1YXJkOmd1YXJkKVxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChST1RBVEUgYWNjb3VudClcXG4gICAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IG9sZC1ndWFyZCB9XFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBvbGQtZ3VhcmQpXFxuXFxuICAgICAgICAodXBkYXRlIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgICAgICB7IFxcXCJndWFyZFxcXCIgOiBuZXctZ3VhcmQgfVxcbiAgICAgICAgICApKSlcXG4gICAgKVxcblxcblxcbiAgKGRlZnVuIHByZWNpc2lvbjppbnRlZ2VyXFxuICAgICgpXFxuICAgIE1JTklNVU1fUFJFQ0lTSU9OKVxcblxcbiAgKGRlZnVuIHRyYW5zZmVyOnN0cmluZyAoc2VuZGVyOnN0cmluZyByZWNlaXZlcjpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcylcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICghPSBzZW5kZXIgcmVjZWl2ZXIpKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgICh3aXRoLXJlYWQgY29pbi10YWJsZSByZWNlaXZlclxcbiAgICAgICAgeyBcXFwiZ3VhcmRcXFwiIDo9IGcgfVxcblxcbiAgICAgICAgKGNyZWRpdCByZWNlaXZlciBnIGFtb3VudCkpXFxuICAgICAgKVxcbiAgICApXFxuXFxuICAoZGVmdW4gdHJhbnNmZXItY3JlYXRlOnN0cmluZ1xcbiAgICAoIHNlbmRlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlcjpzdHJpbmdcXG4gICAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5IGNvbnNlcnZlcy1tYXNzKSBdXFxuXFxuICAgIChlbmZvcmNlICghPSBzZW5kZXIgcmVjZWl2ZXIpXFxuICAgICAgXFxcInNlbmRlciBjYW5ub3QgYmUgdGhlIHJlY2VpdmVyIG9mIGEgdHJhbnNmZXJcXFwiKVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IHJlY2VpdmVyKVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgIFxcXCJ0cmFuc2ZlciBhbW91bnQgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAod2l0aC1jYXBhYmlsaXR5IChUUkFOU0ZFUiBzZW5kZXIgcmVjZWl2ZXIgYW1vdW50KVxcbiAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcbiAgICAgIChjcmVkaXQgcmVjZWl2ZXIgcmVjZWl2ZXItZ3VhcmQgYW1vdW50KSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGNvaW5iYXNlOnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYWNjb3VudC1ndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoZSBpbml0aWFsIGNyZWF0aW9uIG9mIGNvaW5zLiAgVGhpcyBmdW5jdGlvbiBcXFxcXFxuICAgIFxcXFxjYW5ub3QgYmUgdXNlZCBvdXRzaWRlIG9mIHRoZSBjb2luIGNvbnRyYWN0LlxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKD4gYW1vdW50IDAuMCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuICAgIChlbmZvcmNlLXVuaXQgYW1vdW50KVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChDT0lOQkFTRSkpXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgIChjcmVkaXQgYWNjb3VudCBhY2NvdW50LWd1YXJkIGFtb3VudCkpXFxuICAgIClcXG5cXG4gIChkZWZ1biByZW1lZGlhdGU6c3RyaW5nIChhY2NvdW50OnN0cmluZyBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQWxsb3dzIGZvciByZW1lZGlhdGlvbiB0cmFuc2FjdGlvbnMuIFRoaXMgZnVuY3Rpb24gXFxcXFxcbiAgICAgICAgIFxcXFxpcyBwcm90ZWN0ZWQgYnkgdGhlIFJFTUVESUFURSBjYXBhYmlsaXR5XFxcIlxcbiAgICBAbW9kZWwgWyAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICAgIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG5cXG4gICAgKGVuZm9yY2UgKD4gYW1vdW50IDAuMClcXG4gICAgICBcXFwiUmVtZWRpYXRpb24gYW1vdW50IG11c3QgYmUgcG9zaXRpdmVcXFwiKVxcblxcbiAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgKHJlcXVpcmUtY2FwYWJpbGl0eSAoUkVNRURJQVRFKSlcXG4gICAgKHdpdGgtcmVhZCBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6PSBiYWxhbmNlIH1cXG5cXG4gICAgICAoZW5mb3JjZSAoPD0gYW1vdW50IGJhbGFuY2UpIFxcXCJJbnN1ZmZpY2llbnQgZnVuZHNcXFwiKVxcblxcbiAgICAgICh1cGRhdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgtIGJhbGFuY2UgYW1vdW50KSB9XFxuICAgICAgICApKVxcbiAgICApXFxuXFxuICAoZGVmcGFjdCBmdW5kLXR4IChzZW5kZXI6c3RyaW5nIG1pbmVyOnN0cmluZyBtaW5lci1ndWFyZDpndWFyZCB0b3RhbDpkZWNpbWFsKVxcbiAgICBAZG9jIFxcXCInZnVuZC10eCcgaXMgYSBzcGVjaWFsIHBhY3QgdG8gZnVuZCBhIHRyYW5zYWN0aW9uIGluIHR3byBzdGVwcywgICAgIFxcXFxcXG4gICAgXFxcXHdpdGggdGhlIGFjdHVhbCB0cmFuc2FjdGlvbiB0cmFuc3BpcmluZyBpbiB0aGUgbWlkZGxlOiAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXFxcXFxcbiAgICBcXFxcICAxKSBBIGJ1eWluZyBwaGFzZSwgZGViaXRpbmcgdGhlIHNlbmRlciBmb3IgdG90YWwgZ2FzIGFuZCBmZWUsIHlpZWxkaW5nIFxcXFxcXG4gICAgXFxcXCAgICAgVFhfTUFYX0NIQVJHRS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcXFxcXFxuICAgIFxcXFwgIDIpIEEgc2V0dGxlbWVudCBwaGFzZSwgcmVzdW1pbmcgVFhfTUFYX0NIQVJHRSwgYW5kIGFsbG9jYXRpbmcgdG8gdGhlICAgXFxcXFxcbiAgICBcXFxcICAgICBjb2luYmFzZSBhY2NvdW50IGZvciB1c2VkIGdhcyBhbmQgZmVlLCBhbmQgc2VuZGVyIGFjY291bnQgZm9yIGJhbC0gIFxcXFxcXG4gICAgXFxcXCAgICAgYW5jZSAodW51c2VkIGdhcywgaWYgYW55KS5cXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiB0b3RhbCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IG1pbmVyKSlcXG4gICAgICAgICAgICAgOyhwcm9wZXJ0eSBjb25zZXJ2ZXMtbWFzcykgbm90IHN1cHBvcnRlZCB5ZXRcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXAgKGJ1eS1nYXMgc2VuZGVyIHRvdGFsKSlcXG4gICAgKHN0ZXAgKHJlZGVlbS1nYXMgbWluZXIgbWluZXItZ3VhcmQgc2VuZGVyIHRvdGFsKSlcXG4gICAgKVxcblxcbiAgKGRlZnVuIGRlYml0OnN0cmluZyAoYWNjb3VudDpzdHJpbmcgYW1vdW50OmRlY2ltYWwpXFxuICAgIEBkb2MgXFxcIkRlYml0IEFNT1VOVCBmcm9tIEFDQ09VTlQgYmFsYW5jZVxcXCJcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgYWNjb3VudCkpXFxuICAgICAgICAgICBdXFxuXFxuICAgICh2YWxpZGF0ZS1hY2NvdW50IGFjY291bnQpXFxuXFxuICAgIChlbmZvcmNlICg-IGFtb3VudCAwLjApXFxuICAgICAgXFxcImRlYml0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKERFQklUIGFjY291bnQpKVxcbiAgICAod2l0aC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UgfVxcblxcbiAgICAgIChlbmZvcmNlICg8PSBhbW91bnQgYmFsYW5jZSkgXFxcIkluc3VmZmljaWVudCBmdW5kc1xcXCIpXFxuXFxuICAgICAgKHVwZGF0ZSBjb2luLXRhYmxlIGFjY291bnRcXG4gICAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogKC0gYmFsYW5jZSBhbW91bnQpIH1cXG4gICAgICAgICkpXFxuICAgIClcXG5cXG5cXG4gIChkZWZ1biBjcmVkaXQ6c3RyaW5nIChhY2NvdW50OnN0cmluZyBndWFyZDpndWFyZCBhbW91bnQ6ZGVjaW1hbClcXG4gICAgQGRvYyBcXFwiQ3JlZGl0IEFNT1VOVCB0byBBQ0NPVU5UIGJhbGFuY2VcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAoPiBhbW91bnQgMC4wKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IGFjY291bnQpKVxcbiAgICAgICAgICAgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKSBcXFwiY3JlZGl0IGFtb3VudCBtdXN0IGJlIHBvc2l0aXZlXFxcIilcXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChyZXF1aXJlLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KSlcXG4gICAgKHdpdGgtZGVmYXVsdC1yZWFkIGNvaW4tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDogMC4wLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmQgfVxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2UsIFxcXCJndWFyZFxcXCIgOj0gcmV0ZyB9XFxuICAgICAgOyB3ZSBkb24ndCB3YW50IHRvIG92ZXJ3cml0ZSBhbiBleGlzdGluZyBndWFyZCB3aXRoIHRoZSB1c2VyLXN1cHBsaWVkIG9uZVxcbiAgICAgIChlbmZvcmNlICg9IHJldGcgZ3VhcmQpXFxuICAgICAgICBcXFwiYWNjb3VudCBndWFyZHMgZG8gbm90IG1hdGNoXFxcIilcXG5cXG4gICAgICAod3JpdGUgY29pbi10YWJsZSBhY2NvdW50XFxuICAgICAgICB7IFxcXCJiYWxhbmNlXFxcIiA6ICgrIGJhbGFuY2UgYW1vdW50KVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiICAgOiByZXRnXFxuICAgICAgICB9KVxcbiAgICAgICkpXFxuXFxuXFxuICAoZGVmc2NoZW1hIGNyb3NzY2hhaW4tc2NoZW1hXFxuICAgIEBkb2MgXFxcIlNjaGVtYSBmb3IgeWllbGRlZCB2YWx1ZSBpbiBjcm9zcy1jaGFpbiB0cmFuc2ZlcnNcXFwiXFxuICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICByZWNlaXZlci1ndWFyZDpndWFyZFxcbiAgICBhbW91bnQ6ZGVjaW1hbClcXG5cXG4gIChkZWZwYWN0IHRyYW5zZmVyLWNyb3NzY2hhaW46c3RyaW5nXFxuICAgICggc2VuZGVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyOnN0cmluZ1xcbiAgICAgIHJlY2VpdmVyLWd1YXJkOmd1YXJkXFxuICAgICAgdGFyZ2V0LWNoYWluOnN0cmluZ1xcbiAgICAgIGFtb3VudDpkZWNpbWFsIClcXG5cXG4gICAgQG1vZGVsIFsgKHByb3BlcnR5ICg-IGFtb3VudCAwLjApKVxcbiAgICAgICAgICAgICAocHJvcGVydHkgKHZhbGlkLWFjY291bnQgc2VuZGVyKSlcXG4gICAgICAgICAgICAgKHByb3BlcnR5ICh2YWxpZC1hY2NvdW50IHJlY2VpdmVyKSlcXG4gICAgICAgICAgIF1cXG5cXG4gICAgKHN0ZXBcXG4gICAgICAod2l0aC1jYXBhYmlsaXR5IChERUJJVCBzZW5kZXIpXFxuXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCBzZW5kZXIpXFxuICAgICAgICAodmFsaWRhdGUtYWNjb3VudCByZWNlaXZlcilcXG5cXG4gICAgICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiB0YXJnZXQtY2hhaW4pIFxcXCJlbXB0eSB0YXJnZXQtY2hhaW5cXFwiKVxcbiAgICAgICAgKGVuZm9yY2UgKCE9IChhdCAnY2hhaW4taWQgKGNoYWluLWRhdGEpKSB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgIFxcXCJjYW5ub3QgcnVuIGNyb3NzLWNoYWluIHRyYW5zZmVycyB0byB0aGUgc2FtZSBjaGFpblxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZSAoPiBhbW91bnQgMC4wKVxcbiAgICAgICAgICBcXFwidHJhbnNmZXIgcXVhbnRpdHkgbXVzdCBiZSBwb3NpdGl2ZVxcXCIpXFxuXFxuICAgICAgICAoZW5mb3JjZS11bml0IGFtb3VudClcXG5cXG4gICAgICAgIDs7IHN0ZXAgMSAtIGRlYml0IGRlbGV0ZS1hY2NvdW50IG9uIGN1cnJlbnQgY2hhaW5cXG4gICAgICAgIChkZWJpdCBzZW5kZXIgYW1vdW50KVxcblxcbiAgICAgICAgKGxldFxcbiAgICAgICAgICAoKGNyb3NzY2hhaW4tZGV0YWlsczpvYmplY3R7Y3Jvc3NjaGFpbi1zY2hlbWF9XFxuICAgICAgICAgICAgeyBcXFwicmVjZWl2ZXJcXFwiIDogcmVjZWl2ZXJcXG4gICAgICAgICAgICAsIFxcXCJyZWNlaXZlci1ndWFyZFxcXCIgOiByZWNlaXZlci1ndWFyZFxcbiAgICAgICAgICAgICwgXFxcImFtb3VudFxcXCIgOiBhbW91bnRcXG4gICAgICAgICAgICB9KSlcXG4gICAgICAgICAgKHlpZWxkIGNyb3NzY2hhaW4tZGV0YWlscyB0YXJnZXQtY2hhaW4pXFxuICAgICAgICAgICkpKVxcblxcbiAgICAoc3RlcFxcbiAgICAgIChyZXN1bWVcXG4gICAgICAgIHsgXFxcInJlY2VpdmVyXFxcIiA6PSByZWNlaXZlclxcbiAgICAgICAgLCBcXFwicmVjZWl2ZXItZ3VhcmRcXFwiIDo9IHJlY2VpdmVyLWd1YXJkXFxuICAgICAgICAsIFxcXCJhbW91bnRcXFwiIDo9IGFtb3VudFxcbiAgICAgICAgfVxcblxcbiAgICAgICAgOzsgc3RlcCAyIC0gY3JlZGl0IGNyZWF0ZSBhY2NvdW50IG9uIHRhcmdldCBjaGFpblxcbiAgICAgICAgKHdpdGgtY2FwYWJpbGl0eSAoQ1JFRElUIHJlY2VpdmVyKVxcbiAgICAgICAgICAoY3JlZGl0IHJlY2VpdmVyIHJlY2VpdmVyLWd1YXJkIGFtb3VudCkpXFxuICAgICAgICApKVxcbiAgICApXFxuXFxuXFxuICA7IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXFxuICA7IENvaW4gYWxsb2NhdGlvbnNcXG5cXG4gIChkZWZzY2hlbWEgYWxsb2NhdGlvbi1zY2hlbWFcXG4gICAgQGRvYyBcXFwiR2VuZXNpcyBhbGxvY2F0aW9uIHJlZ2lzdHJ5XFxcIlxcbiAgICA7QG1vZGVsIFsgKGludmFyaWFudCAoPj0gYmFsYW5jZSAwLjApKSBdXFxuXFxuICAgIGJhbGFuY2U6ZGVjaW1hbFxcbiAgICBkYXRlOnRpbWVcXG4gICAgZ3VhcmQ6Z3VhcmRcXG4gICAgcmVkZWVtZWQ6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSBhbGxvY2F0aW9uLXRhYmxlOnthbGxvY2F0aW9uLXNjaGVtYX0pXFxuXFxuICAoZGVmdW4gY3JlYXRlLWFsbG9jYXRpb24tYWNjb3VudFxcbiAgICAoIGFjY291bnQ6c3RyaW5nXFxuICAgICAgZGF0ZTp0aW1lXFxuICAgICAga2V5c2V0LXJlZjpzdHJpbmdcXG4gICAgICBhbW91bnQ6ZGVjaW1hbFxcbiAgICApXFxuXFxuICAgIEBkb2MgXFxcIkFkZCBhbiBlbnRyeSB0byB0aGUgY29pbiBhbGxvY2F0aW9uIHRhYmxlLiBUaGlzIGZ1bmN0aW9uIFxcXFxcXG4gICAgICAgICBcXFxcYWxzbyBjcmVhdGVzIGEgY29ycmVzcG9uZGluZyBlbXB0eSBjb2luIGNvbnRyYWN0IGFjY291bnQgXFxcXFxcbiAgICAgICAgIFxcXFxvZiB0aGUgc2FtZSBuYW1lIGFuZCBndWFyZC4gUmVxdWlyZXMgR0VORVNJUyBjYXBhYmlsaXR5LiBcXFwiXFxuXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAocmVxdWlyZS1jYXBhYmlsaXR5IChHRU5FU0lTKSlcXG5cXG4gICAgKHZhbGlkYXRlLWFjY291bnQgYWNjb3VudClcXG4gICAgKGVuZm9yY2UgKD49IGFtb3VudCAwLjApXFxuICAgICAgXFxcImFsbG9jYXRpb24gYW1vdW50IG11c3QgYmUgbm9uLW5lZ2F0aXZlXFxcIilcXG5cXG4gICAgKGVuZm9yY2UtdW5pdCBhbW91bnQpXFxuXFxuICAgIChsZXRcXG4gICAgICAoKGd1YXJkOmd1YXJkIChrZXlzZXQtcmVmLWd1YXJkIGtleXNldC1yZWYpKSlcXG5cXG4gICAgICAoY3JlYXRlLWFjY291bnQgYWNjb3VudCBndWFyZClcXG5cXG4gICAgICAoaW5zZXJ0IGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgICAgeyBcXFwiYmFsYW5jZVxcXCIgOiBhbW91bnRcXG4gICAgICAgICwgXFxcImRhdGVcXFwiIDogZGF0ZVxcbiAgICAgICAgLCBcXFwiZ3VhcmRcXFwiIDogZ3VhcmRcXG4gICAgICAgICwgXFxcInJlZGVlbWVkXFxcIiA6IGZhbHNlXFxuICAgICAgICB9KSkpXFxuXFxuICAoZGVmdW4gcmVsZWFzZS1hbGxvY2F0aW9uXFxuICAgICggYWNjb3VudDpzdHJpbmcgKVxcblxcbiAgICBAZG9jIFxcXCJSZWxlYXNlIGZ1bmRzIGFzc29jaWF0ZWQgd2l0aCBhbGxvY2F0aW9uIEFDQ09VTlQgaW50byBtYWluIGxlZGdlci4gICBcXFxcXFxuICAgICAgICAgXFxcXEFDQ09VTlQgbXVzdCBhbHJlYWR5IGV4aXN0IGluIG1haW4gbGVkZ2VyLiBBbGxvY2F0aW9uIGlzIGRlYWN0aXZhdGVkIFxcXFxcXG4gICAgICAgICBcXFxcYWZ0ZXIgcmVsZWFzZS5cXFwiXFxuICAgIEBtb2RlbCBbIChwcm9wZXJ0eSAodmFsaWQtYWNjb3VudCBhY2NvdW50KSkgXVxcblxcbiAgICAodmFsaWRhdGUtYWNjb3VudCBhY2NvdW50KVxcblxcbiAgICAod2l0aC1yZWFkIGFsbG9jYXRpb24tdGFibGUgYWNjb3VudFxcbiAgICAgIHsgXFxcImJhbGFuY2VcXFwiIDo9IGJhbGFuY2VcXG4gICAgICAsIFxcXCJkYXRlXFxcIiA6PSByZWxlYXNlLXRpbWVcXG4gICAgICAsIFxcXCJyZWRlZW1lZFxcXCIgOj0gcmVkZWVtZWRcXG4gICAgICAsIFxcXCJndWFyZFxcXCIgOj0gZ3VhcmRcXG4gICAgICB9XFxuXFxuICAgICAgKGxldCAoKGN1cnItdGltZTp0aW1lIChhdCAnYmxvY2stdGltZSAoY2hhaW4tZGF0YSkpKSlcXG5cXG4gICAgICAgIChlbmZvcmNlIChub3QgcmVkZWVtZWQpXFxuICAgICAgICAgIFxcXCJhbGxvY2F0aW9uIGZ1bmRzIGhhdmUgYWxyZWFkeSBiZWVuIHJlZGVlbWVkXFxcIilcXG5cXG4gICAgICAgIChlbmZvcmNlXFxuICAgICAgICAgICg-PSBjdXJyLXRpbWUgcmVsZWFzZS10aW1lKVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJmdW5kcyBsb2NrZWQgdW50aWwge30uIGN1cnJlbnQgdGltZToge31cXFwiIFtyZWxlYXNlLXRpbWUgY3Vyci10aW1lXSkpXFxuXFxuICAgICAgICAoZW5mb3JjZS1ndWFyZCBndWFyZClcXG5cXG4gICAgICAgICh3aXRoLWNhcGFiaWxpdHkgKENSRURJVCBhY2NvdW50KVxcbiAgICAgICAgICAoY3JlZGl0IGFjY291bnQgZ3VhcmQgYmFsYW5jZSlcXG5cXG4gICAgICAgICAgKHVwZGF0ZSBhbGxvY2F0aW9uLXRhYmxlIGFjY291bnRcXG4gICAgICAgICAgICB7IFxcXCJyZWRlZW1lZFxcXCIgOiB0cnVlXFxuICAgICAgICAgICAgLCBcXFwiYmFsYW5jZVxcXCIgOiAwLjBcXG4gICAgICAgICAgICB9KVxcblxcbiAgICAgICAgICBcXFwiQWxsb2NhdGlvbiBzdWNjZXNzZnVsbHkgcmVsZWFzZWQgdG8gbWFpbiBsZWRnZXJcXFwiKVxcbiAgICApKSlcXG5cXG4pXFxuKGNyZWF0ZS10YWJsZSBjb2luLXRhYmxlKVxcbihjcmVhdGUtdGFibGUgYWxsb2NhdGlvbi10YWJsZSlcXG4oZW5mb3JjZVxcbiAgKD1cXG4gICAgXFxcInV0X0pfWk5rb3lhUFVFSmhpd1ZlV25rU1FuOUpUOXNRQ1dLZGpqVlZyV29cXFwiXFxuICAgIChhdCAnaGFzaCAoZGVzY3JpYmUtbW9kdWxlICdjb2luKSkpXFxuICBcXFwiaGFzaCBtaXNtYXRjaFxcXCIpXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJjb2luLWNvbnRyYWN0LXYyLXRlbXBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6dHJ1ZX0sInJlcUtleSI6InhKSGF5a3ZhSDA5UFl6TnFTQUxfRVg5azVONHNjMm1DLXZlNk85T0k0MlkiLCJsb2dzIjoiZjVJWUYwQ0todk4zZlRyQkZPWWZEdzNLUE9LUWlQQjlCMG1TQXQyZGdpVSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6dHJ1ZX0sInJlcUtleSI6InhKSGF5a3ZhSDA5UFl6TnFTQUxfRVg5azVONHNjMm1DLXZlNk85T0k0MlkiLCJsb2dzIjoiTHotbnFTY0FBRTNXdmF6bGhwWnZQVzhyRUxqVHp2ZUZoX1o5TFRXTWluNCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjJ9" , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjN9" , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiUHBXOFBIQUotWEtneGVQelQ3dzRldW5oTC1BYjk4S2gyNGxkTTZValdsTSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" , "- - eyJoYXNoIjoiSEhuVGs5N21oUGd6d0gxc0ktMUFrMUljMGQzc3YwNk5ydmpnc2J3SkludyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwic2VuZGVyMDVcIjpbXCJmMDlkOGY2Mzk0YWVhNDI1ZmU2NzgzZDg4Y2Q4MTM2M2Q4MDE3ZjE2YWZkMzcxMWM1NzViZTBmNWNkNWM5YmI5XCJdLFwic2VuZGVyMDRcIjpbXCIyZDcwYWE0ZjY5N2MzYTNiOGRkNmQ5Nzc0NWFjMDc0ZWRjZmQwZWI2NWMzNzc3NGNkZTI1MTM1NDgzYmVhNzFlXCJdLFwibXVsdGktMDItMDMtMDQtYW55XCI6e1wicHJlZFwiOlwia2V5cy1hbnlcIixcImtleXNcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCIsXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCIsXCIyZDcwYWE0ZjY5N2MzYTNiOGRkNmQ5Nzc0NWFjMDc0ZWRjZmQwZWI2NWMzNzc3NGNkZTI1MTM1NDgzYmVhNzFlXCJdfSxcInNlbmRlcjA5XCI6W1wiYzU5ZDk4NDBiMGI2NjA5MDgzNjU0NmI3ZWI0YTczNjA2MjU3NTI3ZWM4YzJiNDgyMzAwZmQyMjkyNjRiMDdlNlwiXSxcImthZC1vcHMtMjBcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIixcImJlMjI5ZjRhOTc1ZTQ0MWRjNjk0ZGVkMGU5MjYwZDk5MzI3MDEyODcwMmZmNWE1YWY3YmVkMmU0MmM5NWNlMDlcIixcIjlhNDg0OTY4N2NiY2ZlYjFmN2M2NTEwNTM5NjM4ZGE1NzYyODk1MDhhZWRjYzc1ZjRkNmFkM2VkMjYyMzYzNWNcIl19LFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJLQURfT1BTXzIwXFxcIiAocmVhZC1rZXlzZXQgXFxcImthZC1vcHMtMjBcXFwiKSAxMC4wKVxcblxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtZ3JhbnRzLWthZG9wc1wifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJISG5Uazk3bWhQZ3p3SDFzSS0xQWsxSWMwZDNzdjA2TnJ2amdzYndKSW53IiwibG9ncyI6ImR3S0s1UGh3WVlheHBoZHJKc3B1UmFHRnBMWm5lRkh5dFlndDRpTUIySUUiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjo1fQ" , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" , "transactionsHash: hjY-SWJcr1XbyfgVAuSgq5O1cP_pMUHzOXK-jOjaryc" - , "outputsHash: 1TnM48xVeQ6-bPJovpJn6QLeey6hTZWKV84QHf2DlmY" - , "payloadHash: UqB64zFBTOSPDTUN4YEwWKdnxqNovSlh3vSAPAxcyp4" + , "outputsHash: 2zKEKsaV9SDFh6YivpstEOz72VwPzFtnDefKgai45fY" + , "payloadHash: Rlm9azJUh5yzvQn91rf59JmMTrbVTrT_iZ5YE3OLN7w" , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" , "" ] diff --git a/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs b/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs index 25c0f06c21..6242a18919 100644 --- a/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs +++ b/src/Chainweb/BlockHeader/Genesis/FastDevelopment0Payload.hs @@ -24,8 +24,8 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IlRhYmxlQ3JlYXRlZCJ9LCJyZXFLZXkiOiI0cU4wUjM4d1R0OEN0bGVJaDU1djdZY3RPYjByc09uWjltd01ONlM0cFg4IiwibG9ncyI6IkwyaUNkQXlYNnptOUxPdXlzTlJ5T0JOS0ozbnlVb1NDR1VWamFyOU1ZZWsiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjozfQ" , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" - , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" + , "- - eyJoYXNoIjoiQ1pteWxXZmllUk1CTXU1dzJFT21wRndLLXQ3YmNPVHM0bUsyYVQzM3VSNCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCIoZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtcHJpbmNpcGFsLW5hbWVzcGFjZTpzdHJpbmdcXG4gICAgICAoIGc6Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBGb3JtYXQgcHJpbmNpcGFsIG5hbWVzcGFjZSBhcyBQYWN0IGhhc2ggKEJMQUtFMmIyNTYpIG9mIHByaW5jaXBhbCBcXFxcXFxuICAgIFxcXFwgaW4gaGV4IHRydW5jYXRlZCB0byAxNjAgYml0cyAoNDAgY2hhcmFjdGVycyksIHByZXBlbmRlZCB3aXRoICduXycuXFxcXFxcbiAgICBcXFxcIE9ubHkgdzogYW5kIGs6IGFjY291bnQgcHJvdG9jb2xzIGFyZSBzdXBwb3J0ZWQuIFxcXCJcXG5cXG4gICAgKGxldFxcbiAgICAgICgodHkgKHR5cGVvZi1wcmluY2lwYWwgKGNyZWF0ZS1wcmluY2lwYWwgZykpKSlcXG5cXG4gICAgICA7OyBvbmx5IHc6IGFuZCBrOiBjdXJyZW50bHkgc3VwcG9ydGVkXFxuICAgICAgKGlmIChvciAoPSB0eSBcXFwiazpcXFwiKSAoPSB0eSBcXFwidzpcXFwiKSlcXG4gICAgICAgICgrIFxcXCJuX1xcXCIgKHRha2UgNDAgKGludC10by1zdHIgMTYgKHN0ci10by1pbnQgNjQgKGhhc2ggZykpKSkpXFxuICAgICAgICAoZW5mb3JjZSBmYWxzZVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJVbnN1cHBvcnRlZCBndWFyZCBwcm90b2NvbDoge31cXFwiIFt0eV0pKVxcbiAgICAgICAgKSlcXG4gIClcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gXFxcXFxcbiAgICBcXFxcIEFsbG93cyBwcmluY2lwYWwgbmFtZXNwYWNlcy4gXFxcXFxcbiAgICBcXFxcIE5vbi1wcmluY2lwYWwgbmFtZXNwYWNlcyByZXF1aXJlIGFjdGl2ZSByb3cgaW4gcmVnaXN0cnkgXFxcXFxcbiAgICBcXFxcIGZvciBOUy1OQU1FIHdpdGggZ3VhcmQgbWF0Y2hpbmcgTlMtQURNSU4uXFxcIlxcblxcbiAgICAoaWYgKD0gKGNyZWF0ZS1wcmluY2lwYWwtbmFtZXNwYWNlIG5zLWFkbWluKSBucy1uYW1lKVxcblxcbiAgICAgIHRydWUgOzsgYWxsb3cgcHJpbmNpcGFsIG5hbWVzcGFjZXNcXG5cXG4gICAgICAod2l0aC1kZWZhdWx0LXJlYWQgcmVnaXN0cnkgbnMtbmFtZSAgICAgICA7OyBvdGhlcndpc2UgZW5mb3JjZSByZWdpc3RyeVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICAgLCAnYWN0aXZlIDogZmFsc2UgfVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQgOj0gYWdcXG4gICAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKVxcbiAgICAgICkpXFxuXFxuICAoZGVmdW4gd3JpdGUtcmVnaXN0cnk6c3RyaW5nXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgZ3VhcmQ6Z3VhcmRcXG4gICAgICAgIGFjdGl2ZTpib29sXFxuICAgICAgICApXFxuICAgIFxcXCIgV3JpdGUgZW50cnkgd2l0aCBHVUFSRCBhbmQgQUNUSVZFIGludG8gcmVnaXN0cnkgZm9yIE5BTUUuIFxcXFxcXG4gICAgXFxcXCBHdWFyZGVkIGJ5IG9wZXJhdGUga2V5c2V0LiBcXFwiXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKE9QRVJBVEUpXFxuXFxuICAgICAgKHZhbGlkYXRlLW5hbWUgbnMtbmFtZSlcXG5cXG4gICAgICAod3JpdGUgcmVnaXN0cnkgbnMtbmFtZVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQ6IGd1YXJkXFxuICAgICAgICAsICdhY3RpdmU6IGFjdGl2ZSB9KVxcblxcbiAgICAgIFxcXCJSZWdpc3RlciBlbnRyeSB3cml0dGVuXFxcIikpXFxuXFxuICAoZGVmdW4gcXVlcnk6b2JqZWN0e3JlZy1lbnRyeX1cXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nIClcXG4gICAgKHJlYWQgcmVnaXN0cnkgbnMtbmFtZSkpXFxuXFxuKVxcblxcbihjcmVhdGUtdGFibGUgcmVnaXN0cnkpXFxuXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpIHRydWUpXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJ1c2VyXFxcIiBHVUFSRF9GQUlMVVJFIHRydWUpXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJmcmVlXFxcIiBHVUFSRF9GQUlMVVJFIHRydWUpXFxuXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImthZGVuYVxcXCJcXG4gIChrZXlzZXQtcmVmLWd1YXJkICducy1vcGVyYXRlLWtleXNldClcXG4gIChrZXlzZXQtcmVmLWd1YXJkICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcInVzZXJcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG4oZGVmaW5lLW5hbWVzcGFjZSBcXFwiZnJlZVxcXCIgR1VBUkRfU1VDQ0VTUyBHVUFSRF9GQUlMVVJFKVxcbjs7cm90YXRlIHRvIHJlYWwgb3BlcmF0ZSBrZXlzZXRcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJsb2FkLW5zLWRldm5ldC1zZW5kZXIwMFwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IkNabXlsV2ZpZVJNQk11NXcyRU9tcEZ3Sy10N2JjT1RzNG1LMmFUMzN1UjQiLCJsb2dzIjoiNHlRZEx6VWU5QUttdko3cnhEdkJtUnVIZjVic0lvSjNBMjZCaGdicl9NOCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" , "- - eyJoYXNoIjoiWE5BNmxPSjFXLTdYWXY1WWI0QXFsNzRveFlJN09KcWpPUVQ1b1k0OHEtZyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wiYWxsb2NhdGlvbi10ZXN0MDFcIjpbXCI3MDExYzM3OTE0MGZmODk5ZjdlNjEzYWMwYTk3ODRhN2MwYTUxNWQzMGZlMGFiMmIwYzA3ZTVhYTE3NjM1ZTNlXCJdLFwiYWxsb2NhdGlvbjAyXCI6W1wiZTllNGU3MWJkMDYzZGNmN2UwNmJkNWIxYTE2Njg4ODk3ZDE1Y2E4YmQyZTUwOWM0NTNjNjE2MjE5YzE4NmNjNVwiXSxcImFsbG9jYXRpb24wMFwiOltcImQ4MmQwZGNkZTk4MjU1MDVkODZhZmI2ZGNjMTA0MTFkNmI2N2E0MjlhNzllMjFiZGE0YmIxMTliZjI4YWI4NzFcIl0sXCJhbGxvY2F0aW9uLXRlc3QwMlwiOltcIjA2NTQ0ZTIyYmZlZjIzMGQ2ZDIyZjk0ODZhYzZjYjc2YmYyNThlYmZiZjAxMzdlZTU4ZjY1NzQzZTFhNWI4YzRcIl0sXCJhbGxvY2F0aW9uMDFcIjpbXCJiNGM4YTNlYTkxZDMxNDZiMDU2MDk5NDc0MGYwZTNlZWQ5MWM1OWQyZWVjYTFkYzk5ZjBjMjg3Mjg0NWMyOTRkXCJdfSxcImNvZGVcIjpcIihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDBcXFwiIChyZWFkLWtleXNldCBcXFwiYWxsb2NhdGlvbjAwXFxcIikpXFxuKGRlZmluZS1rZXlzZXQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDFcXFwiKSlcXG4oZGVmaW5lLWtleXNldCBcXFwiYWxsb2NhdGlvbjAyXFxcIiAocmVhZC1rZXlzZXQgXFxcImFsbG9jYXRpb24wMlxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIpKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWtleXNldHNcIn0ifQ" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IlhOQTZsT0oxVy03WFl2NVliNEFxbDc0b3hZSTdPSnFqT1FUNW9ZNDhxLWciLCJsb2dzIjoiU0t3RUk1NWN4M2pMdU16RWRZQ1NtZ1RSRWhmVjg2alQwVjd3WVdvZWxfQSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjZ9" , "- - eyJoYXNoIjoiNWtxZ0tzWWtwbjZCcS1KUnhNYnA1VG9jUWhRWGRJVVNiM1NGQm1Ba3VWcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMFxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMTVUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMFxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHRpbWUgXFxcIjIxMDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMVxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMlxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24tdGVzdDAxXFxcIiAodGltZSBcXFwiMTkwMC0xMC0zMVQxODowMDowMFpcXFwiKSBcXFwiYWxsb2NhdGlvbi10ZXN0MDFcXFwiIDEwMDAwMDAuMClcXG4oY29pbi5jcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24tdGVzdDAyXFxcIiAyMDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtYWxsb2NhdGlvbnNcIn0ifQ" @@ -33,9 +33,9 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , "- - eyJoYXNoIjoiY2tiMmZnNWVJeXRoS3U5YVN3TkhIWllsWVY2V1R1NGlYaUZDamc5WFFLWSIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwiZTdmN1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIl0sXCJzZW5kZXIwNVwiOltcImYwOWQ4ZjYzOTRhZWE0MjVmZTY3ODNkODhjZDgxMzYzZDgwMTdmMTZhZmQzNzExYzU3NWJlMGY1Y2Q1YzliYjlcIl0sXCJzZW5kZXIwNFwiOltcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl0sXCJtdWx0aS0wMi0wMy0wNC1hbnlcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcIjNhOWRkNTMyZDczZGFjZTE5NWRiYjY0ZDFkYmE2NTcyZmI3ODNkMGZkZDMyNDY4NWUzMmZiZGEyZjg5Zjk5YTZcIixcIjQzZjJhZGIxZGUxOTIwMDBjYjM3NzdiYWNjN2Y5ODNiNjYxNGZkOWMxNzE1Y2Q0NGNkNDg0YjZkM2EwZDM0YzhcIixcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl19LFwic2VuZGVyMDlcIjpbXCJjNTlkOTg0MGIwYjY2MDkwODM2NTQ2YjdlYjRhNzM2MDYyNTc1MjdlYzhjMmI0ODIzMDBmZDIyOTI2NGIwN2U2XCJdLFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXFxuXFxuKGNvaW4uY29pbmJhc2UgXFxcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcXFwiIChyZWFkLWtleXNldCBcXFwiZTdmN1xcXCIpIDE1MC4wKVxcblxcbjsgZW5vdWdoIHRvIGNvdmVyIHRoZSBnYXMgY29zdHMgZm9yIGFsbG9jYXRpb24gcmVsZWFzZVxcbihjb2luLmNvaW5iYXNlIFxcXCJhbGxvY2F0aW9uMDBcXFwiIChrZXlzZXQtcmVmLWd1YXJkIFxcXCJhbGxvY2F0aW9uMDBcXFwiKSAxMDAwMDAuMClcXG4oY29pbi5jb2luYmFzZSBcXFwiYWxsb2NhdGlvbjAxXFxcIiAoa2V5c2V0LXJlZi1ndWFyZCBcXFwiYWxsb2NhdGlvbjAxXFxcIikgMTAwMDAwLjApXFxuKGNvaW4uY29pbmJhc2UgXFxcImFsbG9jYXRpb24wMlxcXCIgKGtleXNldC1yZWYtZ3VhcmQgXFxcImFsbG9jYXRpb24wMlxcXCIpIDEwMDAwMC4wKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWdyYW50czBcIn0ifQ" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJja2IyZmc1ZUl5dGhLdTlhU3dOSEhaWWxZVjZXVHU0aVhpRkNqZzlYUUtZIiwibG9ncyI6ImdMVllyaXhlNHpPZWxpejNkYXlZTDhzWGd0RVVWczJQVndlWGRDYXBnVmsiLCJldmVudHMiOlt7InBhcmFtcyI6WyIiLCJzZW5kZXIwMCIsMTAwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMSIsMTEwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMiIsMTIwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMyIsMTMwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNCIsMTQwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNSIsMTUwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNiIsMTYwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNyIsMTcwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOCIsMTgwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOSIsMTkwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMC0wMSIsMTAxMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMi0wMy0wNC1hbnkiLDEyMzQwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiZTdmNzYzNGU5MjU1NDFmMzY4YjgyN2FkNWM3MjQyMTkwNTEwMGY2MjA1Mjg1YTc4YzE5ZDdiNGEzODcxMTgwNSIsMTUwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJhbGxvY2F0aW9uMDAiLDEwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiYWxsb2NhdGlvbjAxIiwxMDAwMDBdLCJuYW1lIjoiVFJBTlNGRVIiLCJtb2R1bGUiOnsibmFtZXNwYWNlIjpudWxsLCJuYW1lIjoiY29pbiJ9LCJtb2R1bGVIYXNoIjoiTTFnYWJha3FrRWlfMU44ZFJLdDR6NWxFdjFrdUNfbnhMVG55REN1WklLMCJ9LHsicGFyYW1zIjpbIiIsImFsbG9jYXRpb24wMiIsMTAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifV0sIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjh9" , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" - , "transactionsHash: zeAGV98wWLDZvyuggueJs5kMnKo6WuRtHq7BIdzpmRk" - , "outputsHash: 8a5lydbrRwU8SqJN3Tx5T992hP9SWhLWL_WhIUqf5Do" - , "payloadHash: NMxVmmZPifTdTah43F733MN0Eb1i5xBEt6HUGAMNSm4" + , "transactionsHash: EXU6x4Cm1HF93BA4Hh9uzH61Mnl1j_JB__NcoUjdZIo" + , "outputsHash: MK13vACuLm_uHaXeSNGENbNoUtZEwpOjsnqCg1c9zIk" + , "payloadHash: 7SnB_50wLiPPnyNOnnVAyKVwx_fHD1pHtR883GDbDQ0" , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" , "" ] diff --git a/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs b/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs index 2e9a3563c2..4c3843f7f2 100644 --- a/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs +++ b/src/Chainweb/BlockHeader/Genesis/FastDevelopment1to19Payload.hs @@ -24,8 +24,8 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IlRhYmxlQ3JlYXRlZCJ9LCJyZXFLZXkiOiI0cU4wUjM4d1R0OEN0bGVJaDU1djdZY3RPYjByc09uWjltd01ONlM0cFg4IiwibG9ncyI6IkwyaUNkQXlYNnptOUxPdXlzTlJ5T0JOS0ozbnlVb1NDR1VWamFyOU1ZZWsiLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjozfQ" , "- - eyJoYXNoIjoiU0IzVzVFTGl6azl4elNWWk9MX3dsem5VNjh5aUhPQzlwWUhreHBVXzBnbyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihpbnRlcmZhY2UgZ2FzLXBheWVyLXYxXFxuXFxuICAoZGVmY2FwIEdBU19QQVlFUjpib29sXFxuICAgICggdXNlcjpzdHJpbmdcXG4gICAgICBsaW1pdDppbnRlZ2VyXFxuICAgICAgcHJpY2U6ZGVjaW1hbFxcbiAgICApXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgY2FwYWJpbGl0eSBpbmRpY2F0aW5nIHRoYXQgZGVjbGFyaW5nIG1vZHVsZSBzdXBwb3J0cyBcXFxcXFxuICAgIFxcXFwgZ2FzIHBheW1lbnQgZm9yIFVTRVIgZm9yIGdhcyBMSU1JVCBhbmQgUFJJQ0UuIEZ1bmN0aW9uYWxpdHkgXFxcXFxcbiAgICBcXFxcIHNob3VsZCByZXF1aXJlIGNhcGFiaWxpdHkgKGNvaW4uRlVORF9UWCksIGFuZCBzaG91bGQgdmFsaWRhdGUgXFxcXFxcbiAgICBcXFxcIHRoZSBzcGVuZCBvZiAobGltaXQgKiBwcmljZSksIHBvc3NpYmx5IHVwZGF0aW5nIHNvbWUgZGF0YWJhc2UgXFxcXFxcbiAgICBcXFxcIGVudHJ5LiBcXFxcXFxuICAgIFxcXFwgU2hvdWxkIGNvbXBvc2UgY2FwYWJpbGl0eSByZXF1aXJlZCBmb3IgJ2NyZWF0ZS1nYXMtcGF5ZXItZ3VhcmQnLlxcXCJcXG4gICAgQG1vZGVsXFxuICAgIFsgKHByb3BlcnR5ICh1c2VyICE9IFxcXCJcXFwiKSlcXG4gICAgICAocHJvcGVydHkgKGxpbWl0ID4gMCkpXFxuICAgICAgKHByb3BlcnR5IChwcmljZSA-IDAuMCkpXFxuICAgIF1cXG4gIClcXG5cXG4gIChkZWZ1biBjcmVhdGUtZ2FzLXBheWVyLWd1YXJkOmd1YXJkICgpXFxuICAgIEBkb2NcXG4gICAgXFxcIiBQcm92aWRlIGEgZ3VhcmQgc3VpdGFibGUgZm9yIGNvbnRyb2xsaW5nIGEgY29pbiBhY2NvdW50IHRoYXQgY2FuIFxcXFxcXG4gICAgXFxcXCBwYXkgZ2FzIHZpYSBHQVNfUEFZRVIgbWVjaGFuaWNzLiBHZW5lcmFsbHkgdGhpcyBpcyBhY2NvbXBsaXNoZWQgXFxcXFxcbiAgICBcXFxcIGJ5IGhhdmluZyBHQVNfUEFZRVIgY29tcG9zZSBhbiB1bnBhcmFtZXRlcml6ZWQsIHVubWFuYWdlZCBjYXBhYmlsaXR5IFxcXFxcXG4gICAgXFxcXCB0aGF0IGlzIHJlcXVpcmVkIGluIHRoaXMgZ3VhcmQuIFRodXMsIGlmIGNvaW4gY29udHJhY3QgaXMgYWJsZSB0byBcXFxcXFxuICAgIFxcXFwgc3VjY2Vzc2Z1bGx5IGFjcXVpcmUgR0FTX1BBWUVSLCB0aGUgY29tcG9zZWQgJ2Fub255bW91cycgY2FwIHJlcXVpcmVkIFxcXFxcXG4gICAgXFxcXCBoZXJlIHdpbGwgYmUgaW4gc2NvcGUsIGFuZCBnYXMgYnV5IHdpbGwgc3VjY2VlZC5cXFwiXFxuICApXFxuXFxuKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZ2VuZXNpcy0wMVwifSJ9" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IkxvYWRlZCBpbnRlcmZhY2UgZ2FzLXBheWVyLXYxIn0sInJlcUtleSI6IlNCM1c1RUxpems5eHpTVlpPTF93bHpuVTY4eWlIT0M5cFlIa3hwVV8wZ28iLCJsb2dzIjoiZlZuSFlta19QNmJSY3VjeVg1RDdLamNLYkVsVDlEcU9vZW9yUFEtUXdsMCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjR9" - , "- - eyJoYXNoIjoia2ZMd2Y2a0FzdEVnc0NLYnZPOHR2YTNnWktBWXgzT0dHYTZYRURMaU9hMCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCJcXG4oZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gUmVxdWlyZXMgYWN0aXZlIHJvdyBpbiByZWdpc3RyeSBcXFxcXFxuICAgIFxcXFwgZm9yIE5TLU5BTUUgd2l0aCBndWFyZCBtYXRjaGluZyBOUy1BRE1JTi5cXFwiXFxuXFxuICAgICh2YWxpZGF0ZS1uYW1lIG5zLW5hbWUpXFxuXFxuICAgICh3aXRoLWRlZmF1bHQtcmVhZCByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICwgJ2FjdGl2ZSA6IGZhbHNlIH1cXG4gICAgICB7ICdhZG1pbi1ndWFyZCA6PSBhZ1xcbiAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKSlcXG5cXG4gIChkZWZ1biB3cml0ZS1yZWdpc3RyeTpzdHJpbmdcXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nXFxuICAgICAgICBndWFyZDpndWFyZFxcbiAgICAgICAgYWN0aXZlOmJvb2xcXG4gICAgICAgIClcXG4gICAgXFxcIiBXcml0ZSBlbnRyeSB3aXRoIEdVQVJEIGFuZCBBQ1RJVkUgaW50byByZWdpc3RyeSBmb3IgTkFNRS4gXFxcXFxcbiAgICBcXFxcIEd1YXJkZWQgYnkgb3BlcmF0ZSBrZXlzZXQuIFxcXCJcXG5cXG4gICAgKHdpdGgtY2FwYWJpbGl0eSAoT1BFUkFURSlcXG5cXG4gICAgICAodmFsaWRhdGUtbmFtZSBucy1uYW1lKVxcblxcbiAgICAgICh3cml0ZSByZWdpc3RyeSBucy1uYW1lXFxuICAgICAgICB7ICdhZG1pbi1ndWFyZDogZ3VhcmRcXG4gICAgICAgICwgJ2FjdGl2ZTogYWN0aXZlIH0pXFxuXFxuICAgICAgXFxcIlJlZ2lzdGVyIGVudHJ5IHdyaXR0ZW5cXFwiKSlcXG5cXG4gIChkZWZ1biBxdWVyeTpvYmplY3R7cmVnLWVudHJ5fVxcbiAgICAgICggbnMtbmFtZTpzdHJpbmcgKVxcbiAgICAocmVhZCByZWdpc3RyeSBucy1uYW1lKSlcXG5cXG4gIClcXG5cXG4oY3JlYXRlLXRhYmxlIHJlZ2lzdHJ5KVxcblxcbih3cml0ZS1yZWdpc3RyeSBcXFwia2FkZW5hXFxcIlxcbiAgKGtleXNldC1yZWYtZ3VhcmQgJ25zLW9wZXJhdGUta2V5c2V0KSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwidXNlclxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcbih3cml0ZS1yZWdpc3RyeSBcXFwiZnJlZVxcXCIgR1VBUkRfRkFJTFVSRSB0cnVlKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblxcbihkZWZpbmUtbmFtZXNwYWNlIFxcXCJ1c2VyXFxcIiBHVUFSRF9TVUNDRVNTIEdVQVJEX0ZBSUxVUkUpXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImZyZWVcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG47O3JvdGF0ZSB0byByZWFsIG9wZXJhdGUga2V5c2V0XFxuKGRlZmluZS1rZXlzZXQgJ25zLW9wZXJhdGUta2V5c2V0IChyZWFkLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQpKVxcblwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwibG9hZC1ucy1kZXZuZXQtc2VuZGVyMDBcIn0ifQ" - , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6ImtmTHdmNmtBc3RFZ3NDS2J2Tzh0dmEzZ1pLQVl4M09HR2E2WEVETGlPYTAiLCJsb2dzIjoiZlJZZ3huUkQ4eUIyY041V3lYSGx3d180Snp0RkhQQ3FIZEI1UGM4WW90TSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" + , "- - eyJoYXNoIjoiQ1pteWxXZmllUk1CTXU1dzJFT21wRndLLXQ3YmNPVHM0bUsyYVQzM3VSNCIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wibnMtYWRtaW4ta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLW9wZXJhdGUta2V5c2V0XCI6W1wiMzY4ODIwZjgwYzMyNGJiYzdjMmIwNjEwNjg4YTdkYTQzZTM5ZjkxZDExODczMjY3MWNkOWM3NTAwZmY0M2NjYVwiXSxcIm5zLWdlbmVzaXMta2V5c2V0XCI6e1wicHJlZFwiOlwiPVwiLFwia2V5c1wiOltdfX0sXCJjb2RlXCI6XCIoZGVmaW5lLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0IChyZWFkLWtleXNldCAnbnMtYWRtaW4ta2V5c2V0KSlcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1nZW5lc2lzLWtleXNldCkpXFxuXFxuKG1vZHVsZSBucyBHT1ZFUk5BTkNFXFxuICBcXFwiQWRtaW5pc3RlcnMgZGVmaW5pdGlvbiBvZiBuZXcgbmFtZXNwYWNlcyBpbiBDaGFpbndlYi5cXFwiXFxuXFxuICAoZGVmc2NoZW1hIHJlZy1lbnRyeVxcbiAgICBhZG1pbi1ndWFyZDpndWFyZFxcbiAgICBhY3RpdmU6Ym9vbClcXG5cXG4gIChkZWZ0YWJsZSByZWdpc3RyeTp7cmVnLWVudHJ5fSlcXG5cXG4gIChkZWZjYXAgR09WRVJOQU5DRSAoKVxcbiAgICAoZW5mb3JjZS1rZXlzZXQgJ25zLWFkbWluLWtleXNldCkpXFxuXFxuICAoZGVmY2FwIE9QRVJBVEUgKClcXG4gICAgKGVuZm9yY2Uta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuICAoZGVmY29uc3QgR1VBUkRfU1VDQ0VTUyAoY3JlYXRlLXVzZXItZ3VhcmQgKHN1Y2Nlc3MpKSlcXG4gIChkZWZjb25zdCBHVUFSRF9GQUlMVVJFIChjcmVhdGUtdXNlci1ndWFyZCAoZmFpbHVyZSkpKVxcblxcbiAgKGRlZnVuIHN1Y2Nlc3MgKClcXG4gICAgdHJ1ZSlcXG4gIChkZWZ1biBmYWlsdXJlICgpXFxuICAgIChlbmZvcmNlIGZhbHNlIFxcXCJEaXNhYmxlZFxcXCIpKVxcblxcbiAgKGRlZnVuIHZhbGlkYXRlLW5hbWUgKG5hbWUpXFxuICAgIChlbmZvcmNlICghPSBcXFwiXFxcIiBuYW1lKSBcXFwiRW1wdHkgbmFtZSBub3QgYWxsb3dlZFxcXCIpXFxuICAgIChlbmZvcmNlICg8IChsZW5ndGggbmFtZSkgNjQpIFxcXCJOYW1lIG11c3QgYmUgbGVzcyB0aGFuIDY0IGNoYXJhY3RlcnMgbG9uZ1xcXCIpXFxuICAgIChlbmZvcmNlIChpcy1jaGFyc2V0IENIQVJTRVRfTEFUSU4xIG5hbWUpXFxuICAgICAgICAgICAgIFxcXCJOYW1lIG11c3QgYmUgaW4gbGF0aW4xIGNoYXJzZXRcXFwiKSlcXG5cXG4gIChkZWZ1biBjcmVhdGUtcHJpbmNpcGFsLW5hbWVzcGFjZTpzdHJpbmdcXG4gICAgICAoIGc6Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBGb3JtYXQgcHJpbmNpcGFsIG5hbWVzcGFjZSBhcyBQYWN0IGhhc2ggKEJMQUtFMmIyNTYpIG9mIHByaW5jaXBhbCBcXFxcXFxuICAgIFxcXFwgaW4gaGV4IHRydW5jYXRlZCB0byAxNjAgYml0cyAoNDAgY2hhcmFjdGVycyksIHByZXBlbmRlZCB3aXRoICduXycuXFxcXFxcbiAgICBcXFxcIE9ubHkgdzogYW5kIGs6IGFjY291bnQgcHJvdG9jb2xzIGFyZSBzdXBwb3J0ZWQuIFxcXCJcXG5cXG4gICAgKGxldFxcbiAgICAgICgodHkgKHR5cGVvZi1wcmluY2lwYWwgKGNyZWF0ZS1wcmluY2lwYWwgZykpKSlcXG5cXG4gICAgICA7OyBvbmx5IHc6IGFuZCBrOiBjdXJyZW50bHkgc3VwcG9ydGVkXFxuICAgICAgKGlmIChvciAoPSB0eSBcXFwiazpcXFwiKSAoPSB0eSBcXFwidzpcXFwiKSlcXG4gICAgICAgICgrIFxcXCJuX1xcXCIgKHRha2UgNDAgKGludC10by1zdHIgMTYgKHN0ci10by1pbnQgNjQgKGhhc2ggZykpKSkpXFxuICAgICAgICAoZW5mb3JjZSBmYWxzZVxcbiAgICAgICAgICAoZm9ybWF0IFxcXCJVbnN1cHBvcnRlZCBndWFyZCBwcm90b2NvbDoge31cXFwiIFt0eV0pKVxcbiAgICAgICAgKSlcXG4gIClcXG5cXG4gIChkZWZ1biB2YWxpZGF0ZTpib29sXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgbnMtYWRtaW46Z3VhcmRcXG4gICAgICAgIClcXG4gICAgXFxcIiBNYW5hZ2VzIG5hbWVzcGFjZSBpbnN0YWxsIGZvciBDaGFpbndlYi4gXFxcXFxcbiAgICBcXFxcIEFsbG93cyBwcmluY2lwYWwgbmFtZXNwYWNlcy4gXFxcXFxcbiAgICBcXFxcIE5vbi1wcmluY2lwYWwgbmFtZXNwYWNlcyByZXF1aXJlIGFjdGl2ZSByb3cgaW4gcmVnaXN0cnkgXFxcXFxcbiAgICBcXFxcIGZvciBOUy1OQU1FIHdpdGggZ3VhcmQgbWF0Y2hpbmcgTlMtQURNSU4uXFxcIlxcblxcbiAgICAoaWYgKD0gKGNyZWF0ZS1wcmluY2lwYWwtbmFtZXNwYWNlIG5zLWFkbWluKSBucy1uYW1lKVxcblxcbiAgICAgIHRydWUgOzsgYWxsb3cgcHJpbmNpcGFsIG5hbWVzcGFjZXNcXG5cXG4gICAgICAod2l0aC1kZWZhdWx0LXJlYWQgcmVnaXN0cnkgbnMtbmFtZSAgICAgICA7OyBvdGhlcndpc2UgZW5mb3JjZSByZWdpc3RyeVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQgOiBucy1hZG1pblxcbiAgICAgICAgLCAnYWN0aXZlIDogZmFsc2UgfVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQgOj0gYWdcXG4gICAgICAgICwgJ2FjdGl2ZSA6PSBpcy1hY3RpdmUgfVxcblxcbiAgICAgICAgKGVuZm9yY2UgaXMtYWN0aXZlIFxcXCJJbmFjdGl2ZSBvciB1bnJlZ2lzdGVyZWQgbmFtZXNwYWNlXFxcIilcXG4gICAgICAgIChlbmZvcmNlICg9IG5zLWFkbWluIGFnKSBcXFwiQWRtaW4gZ3VhcmQgbXVzdCBtYXRjaCBndWFyZCBpbiByZWdpc3RyeVxcXCIpXFxuXFxuICAgICAgICB0cnVlKVxcbiAgICAgICkpXFxuXFxuICAoZGVmdW4gd3JpdGUtcmVnaXN0cnk6c3RyaW5nXFxuICAgICAgKCBucy1uYW1lOnN0cmluZ1xcbiAgICAgICAgZ3VhcmQ6Z3VhcmRcXG4gICAgICAgIGFjdGl2ZTpib29sXFxuICAgICAgICApXFxuICAgIFxcXCIgV3JpdGUgZW50cnkgd2l0aCBHVUFSRCBhbmQgQUNUSVZFIGludG8gcmVnaXN0cnkgZm9yIE5BTUUuIFxcXFxcXG4gICAgXFxcXCBHdWFyZGVkIGJ5IG9wZXJhdGUga2V5c2V0LiBcXFwiXFxuXFxuICAgICh3aXRoLWNhcGFiaWxpdHkgKE9QRVJBVEUpXFxuXFxuICAgICAgKHZhbGlkYXRlLW5hbWUgbnMtbmFtZSlcXG5cXG4gICAgICAod3JpdGUgcmVnaXN0cnkgbnMtbmFtZVxcbiAgICAgICAgeyAnYWRtaW4tZ3VhcmQ6IGd1YXJkXFxuICAgICAgICAsICdhY3RpdmU6IGFjdGl2ZSB9KVxcblxcbiAgICAgIFxcXCJSZWdpc3RlciBlbnRyeSB3cml0dGVuXFxcIikpXFxuXFxuICAoZGVmdW4gcXVlcnk6b2JqZWN0e3JlZy1lbnRyeX1cXG4gICAgICAoIG5zLW5hbWU6c3RyaW5nIClcXG4gICAgKHJlYWQgcmVnaXN0cnkgbnMtbmFtZSkpXFxuXFxuKVxcblxcbihjcmVhdGUtdGFibGUgcmVnaXN0cnkpXFxuXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJrYWRlbmFcXFwiXFxuICAoa2V5c2V0LXJlZi1ndWFyZCAnbnMtb3BlcmF0ZS1rZXlzZXQpIHRydWUpXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJ1c2VyXFxcIiBHVUFSRF9GQUlMVVJFIHRydWUpXFxuKHdyaXRlLXJlZ2lzdHJ5IFxcXCJmcmVlXFxcIiBHVUFSRF9GQUlMVVJFIHRydWUpXFxuXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcImthZGVuYVxcXCJcXG4gIChrZXlzZXQtcmVmLWd1YXJkICducy1vcGVyYXRlLWtleXNldClcXG4gIChrZXlzZXQtcmVmLWd1YXJkICducy1vcGVyYXRlLWtleXNldCkpXFxuXFxuKGRlZmluZS1uYW1lc3BhY2UgXFxcInVzZXJcXFwiIEdVQVJEX1NVQ0NFU1MgR1VBUkRfRkFJTFVSRSlcXG4oZGVmaW5lLW5hbWVzcGFjZSBcXFwiZnJlZVxcXCIgR1VBUkRfU1VDQ0VTUyBHVUFSRF9GQUlMVVJFKVxcbjs7cm90YXRlIHRvIHJlYWwgb3BlcmF0ZSBrZXlzZXRcXG4oZGVmaW5lLWtleXNldCAnbnMtb3BlcmF0ZS1rZXlzZXQgKHJlYWQta2V5c2V0ICducy1vcGVyYXRlLWtleXNldCkpXFxuXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJsb2FkLW5zLWRldm5ldC1zZW5kZXIwMFwifSJ9" + , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IkNabXlsV2ZpZVJNQk11NXcyRU9tcEZ3Sy10N2JjT1RzNG1LMmFUMzN1UjQiLCJsb2dzIjoiNHlRZEx6VWU5QUttdko3cnhEdkJtUnVIZjVic0lvSjNBMjZCaGdicl9NOCIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjV9" , "- - eyJoYXNoIjoiWE5BNmxPSjFXLTdYWXY1WWI0QXFsNzRveFlJN09KcWpPUVQ1b1k0OHEtZyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wiYWxsb2NhdGlvbi10ZXN0MDFcIjpbXCI3MDExYzM3OTE0MGZmODk5ZjdlNjEzYWMwYTk3ODRhN2MwYTUxNWQzMGZlMGFiMmIwYzA3ZTVhYTE3NjM1ZTNlXCJdLFwiYWxsb2NhdGlvbjAyXCI6W1wiZTllNGU3MWJkMDYzZGNmN2UwNmJkNWIxYTE2Njg4ODk3ZDE1Y2E4YmQyZTUwOWM0NTNjNjE2MjE5YzE4NmNjNVwiXSxcImFsbG9jYXRpb24wMFwiOltcImQ4MmQwZGNkZTk4MjU1MDVkODZhZmI2ZGNjMTA0MTFkNmI2N2E0MjlhNzllMjFiZGE0YmIxMTliZjI4YWI4NzFcIl0sXCJhbGxvY2F0aW9uLXRlc3QwMlwiOltcIjA2NTQ0ZTIyYmZlZjIzMGQ2ZDIyZjk0ODZhYzZjYjc2YmYyNThlYmZiZjAxMzdlZTU4ZjY1NzQzZTFhNWI4YzRcIl0sXCJhbGxvY2F0aW9uMDFcIjpbXCJiNGM4YTNlYTkxZDMxNDZiMDU2MDk5NDc0MGYwZTNlZWQ5MWM1OWQyZWVjYTFkYzk5ZjBjMjg3Mjg0NWMyOTRkXCJdfSxcImNvZGVcIjpcIihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDBcXFwiIChyZWFkLWtleXNldCBcXFwiYWxsb2NhdGlvbjAwXFxcIikpXFxuKGRlZmluZS1rZXlzZXQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uMDFcXFwiKSlcXG4oZGVmaW5lLWtleXNldCBcXFwiYWxsb2NhdGlvbjAyXFxcIiAocmVhZC1rZXlzZXQgXFxcImFsbG9jYXRpb24wMlxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMVxcXCIpKVxcbihkZWZpbmUta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIpKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWtleXNldHNcIn0ifQ" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IktleXNldCBkZWZpbmVkIn0sInJlcUtleSI6IlhOQTZsT0oxVy03WFl2NVliNEFxbDc0b3hZSTdPSnFqT1FUNW9ZNDhxLWciLCJsb2dzIjoiU0t3RUk1NWN4M2pMdU16RWRZQ1NtZ1RSRWhmVjg2alQwVjd3WVdvZWxfQSIsIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjZ9" , "- - eyJoYXNoIjoiNWtxZ0tzWWtwbjZCcS1KUnhNYnA1VG9jUWhRWGRJVVNiM1NGQm1Ba3VWcyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6bnVsbCxcImNvZGVcIjpcIihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMFxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMTVUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMFxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMVxcXCIgKHRpbWUgXFxcIjIxMDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMVxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24wMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24wMlxcXCIgMTAwMDAwMC4wKVxcbihjb2luLmNyZWF0ZS1hbGxvY2F0aW9uLWFjY291bnQgXFxcImFsbG9jYXRpb24tdGVzdDAxXFxcIiAodGltZSBcXFwiMTkwMC0xMC0zMVQxODowMDowMFpcXFwiKSBcXFwiYWxsb2NhdGlvbi10ZXN0MDFcXFwiIDEwMDAwMDAuMClcXG4oY29pbi5jcmVhdGUtYWxsb2NhdGlvbi1hY2NvdW50IFxcXCJhbGxvY2F0aW9uLXRlc3QwMlxcXCIgKHRpbWUgXFxcIjE5MDAtMTAtMzFUMTg6MDA6MDBaXFxcIikgXFxcImFsbG9jYXRpb24tdGVzdDAyXFxcIiAyMDAwMDAwLjApXCJ9fSxcInNpZ25lcnNcIjpbXSxcIm1ldGFcIjp7XCJjcmVhdGlvblRpbWVcIjowLFwidHRsXCI6MTcyODAwLFwiZ2FzTGltaXRcIjowLFwiY2hhaW5JZFwiOlwiXCIsXCJnYXNQcmljZVwiOjAsXCJzZW5kZXJcIjpcIlwifSxcIm5vbmNlXCI6XCJkZXZuZXQtYWxsb2NhdGlvbnNcIn0ifQ" @@ -33,9 +33,9 @@ payloadBlock = fromJuste $ decodeThrow $ encodeUtf8 $ T.unlines , "- - eyJoYXNoIjoiZjIxVTFJRnlRN0ZfLTRlTnR6QTFuWnZjQ0g5cnY5ZnZsNWM1a0RyNXFsdyIsInNpZ3MiOltdLCJjbWQiOiJ7XCJuZXR3b3JrSWRcIjpudWxsLFwicGF5bG9hZFwiOntcImV4ZWNcIjp7XCJkYXRhXCI6e1wic2VuZGVyMDdcIjpbXCI0YzMxZGM5ZWU3ZjI0MTc3Zjc4YjZmNTE4MDEyYTIwODMyNmUyYWYxZjM3YmIwYTI0MDViNTA1NmQwY2FkNjI4XCJdLFwic2VuZGVyMDFcIjpbXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDZcIjpbXCI1ZmZjMWY3ZmVmN2E0NDczODYyNTc2MmY3NWE0MjI5NDU0OTUxZTAzZjJhZmM2ZjgxMzA5YzBjMWJkZjllZTZmXCJdLFwic2VuZGVyMDBcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCJdLFwiZTdmN1wiOltcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcIl0sXCJzZW5kZXIwNVwiOltcImYwOWQ4ZjYzOTRhZWE0MjVmZTY3ODNkODhjZDgxMzYzZDgwMTdmMTZhZmQzNzExYzU3NWJlMGY1Y2Q1YzliYjlcIl0sXCJzZW5kZXIwNFwiOltcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl0sXCJtdWx0aS0wMi0wMy0wNC1hbnlcIjp7XCJwcmVkXCI6XCJrZXlzLWFueVwiLFwia2V5c1wiOltcIjNhOWRkNTMyZDczZGFjZTE5NWRiYjY0ZDFkYmE2NTcyZmI3ODNkMGZkZDMyNDY4NWUzMmZiZGEyZjg5Zjk5YTZcIixcIjQzZjJhZGIxZGUxOTIwMDBjYjM3NzdiYWNjN2Y5ODNiNjYxNGZkOWMxNzE1Y2Q0NGNkNDg0YjZkM2EwZDM0YzhcIixcIjJkNzBhYTRmNjk3YzNhM2I4ZGQ2ZDk3NzQ1YWMwNzRlZGNmZDBlYjY1YzM3Nzc0Y2RlMjUxMzU0ODNiZWE3MWVcIl19LFwic2VuZGVyMDlcIjpbXCJjNTlkOTg0MGIwYjY2MDkwODM2NTQ2YjdlYjRhNzM2MDYyNTc1MjdlYzhjMmI0ODIzMDBmZDIyOTI2NGIwN2U2XCJdLFwic2VuZGVyMDNcIjpbXCI0M2YyYWRiMWRlMTkyMDAwY2IzNzc3YmFjYzdmOTgzYjY2MTRmZDljMTcxNWNkNDRjZDQ4NGI2ZDNhMGQzNGM4XCJdLFwibXVsdGktMDAtMDFcIjpbXCIzNjg4MjBmODBjMzI0YmJjN2MyYjA2MTA2ODhhN2RhNDNlMzlmOTFkMTE4NzMyNjcxY2Q5Yzc1MDBmZjQzY2NhXCIsXCI2YmUyZjQ4NWE3YWY3NWZlZGI0YjdmMTUzYTkwM2Y3ZTYwMDBjYTRhYTUwMTE3OWM5MWEyNDUwYjc3N2JkMmE3XCJdLFwic2VuZGVyMDhcIjpbXCI2M2IyZWJhNGVkNzBkNDYxMmQzZTdiYzkwZGIyZmJmNGM3NmY3YjA3NDM2M2U4NmQ3M2YwYmM2MTdmOGU4YjgxXCJdLFwic2VuZGVyMDJcIjpbXCIzYTlkZDUzMmQ3M2RhY2UxOTVkYmI2NGQxZGJhNjU3MmZiNzgzZDBmZGQzMjQ2ODVlMzJmYmRhMmY4OWY5OWE2XCJdfSxcImNvZGVcIjpcIihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMFxcXCIpIDEwMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMVxcXCIpIDExMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwMlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwMlxcXCIpIDEyMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwM1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwM1xcXCIpIDEzMDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNFxcXCIpIDE0MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNVxcXCIpIDE1MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwNlxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwNlxcXCIpIDE2MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwN1xcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwN1xcXCIpIDE3MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOFxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOFxcXCIpIDE4MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJzZW5kZXIwOVxcXCIgKHJlYWQta2V5c2V0IFxcXCJzZW5kZXIwOVxcXCIpIDE5MDAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMC0wMVxcXCIgKHJlYWQta2V5c2V0IFxcXCJtdWx0aS0wMC0wMVxcXCIpIDEwMTAwMDAwMC4wKVxcbihjb2luLmNvaW5iYXNlIFxcXCJtdWx0aS0wMi0wMy0wNC1hbnlcXFwiIChyZWFkLWtleXNldCBcXFwibXVsdGktMDItMDMtMDQtYW55XFxcIikgMTIzNDAwMDAwLjApXFxuXFxuKGNvaW4uY29pbmJhc2UgXFxcImU3Zjc2MzRlOTI1NTQxZjM2OGI4MjdhZDVjNzI0MjE5MDUxMDBmNjIwNTI4NWE3OGMxOWQ3YjRhMzg3MTE4MDVcXFwiIChyZWFkLWtleXNldCBcXFwiZTdmN1xcXCIpIDE1MC4wKVwifX0sXCJzaWduZXJzXCI6W10sXCJtZXRhXCI6e1wiY3JlYXRpb25UaW1lXCI6MCxcInR0bFwiOjE3MjgwMCxcImdhc0xpbWl0XCI6MCxcImNoYWluSWRcIjpcIlwiLFwiZ2FzUHJpY2VcIjowLFwic2VuZGVyXCI6XCJcIn0sXCJub25jZVwiOlwiZGV2bmV0LWdyYW50c05cIn0ifQ" , " - eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6IldyaXRlIHN1Y2NlZWRlZCJ9LCJyZXFLZXkiOiJmMjFVMUlGeVE3Rl8tNGVOdHpBMW5admNDSDlydjlmdmw1YzVrRHI1cWx3IiwibG9ncyI6Iks5dDlKdFBZc1hGQ0dhNk5TTEI2V2xJdTFZaEJqWG94bTFPZkY2Y0xFd00iLCJldmVudHMiOlt7InBhcmFtcyI6WyIiLCJzZW5kZXIwMCIsMTAwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMSIsMTEwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMiIsMTIwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwMyIsMTMwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNCIsMTQwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNSIsMTUwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNiIsMTYwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwNyIsMTcwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOCIsMTgwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJzZW5kZXIwOSIsMTkwMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMC0wMSIsMTAxMDAwMDAwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifSx7InBhcmFtcyI6WyIiLCJtdWx0aS0wMi0wMy0wNC1hbnkiLDEyMzQwMDAwMF0sIm5hbWUiOiJUUkFOU0ZFUiIsIm1vZHVsZSI6eyJuYW1lc3BhY2UiOm51bGwsIm5hbWUiOiJjb2luIn0sIm1vZHVsZUhhc2giOiJNMWdhYmFrcWtFaV8xTjhkUkt0NHo1bEV2MWt1Q19ueExUbnlEQ3VaSUswIn0seyJwYXJhbXMiOlsiIiwiZTdmNzYzNGU5MjU1NDFmMzY4YjgyN2FkNWM3MjQyMTkwNTEwMGY2MjA1Mjg1YTc4YzE5ZDdiNGEzODcxMTgwNSIsMTUwXSwibmFtZSI6IlRSQU5TRkVSIiwibW9kdWxlIjp7Im5hbWVzcGFjZSI6bnVsbCwibmFtZSI6ImNvaW4ifSwibW9kdWxlSGFzaCI6Ik0xZ2FiYWtxa0VpXzFOOGRSS3Q0ejVsRXYxa3VDX254TFRueURDdVpJSzAifV0sIm1ldGFEYXRhIjpudWxsLCJjb250aW51YXRpb24iOm51bGwsInR4SWQiOjh9" , "minerData: eyJhY2NvdW50IjoiTm9NaW5lciIsInByZWRpY2F0ZSI6IjwiLCJwdWJsaWMta2V5cyI6W119" - , "transactionsHash: XndbRFLq7is65RKueTygrkIbP07oitsnNdv9f9Q-_Uo" - , "outputsHash: Pa_j87P-788AYgWVCTZCru_xQ5vcFmkWKXhARgTJphU" - , "payloadHash: b0gX32KDdIRRqSTDJgonbGzFKnlTDzOBULlXmoPiiEM" + , "transactionsHash: AVnFzuQg8IT6i-n0IaC8lWxDcVK3ZPwkjMg39ptnpDU" + , "outputsHash: NZGYoaBtkVd0XqjkKp7d7iYcpysds3pjOs4cb_IROA8" + , "payloadHash: 0DAhfA_HrYjO0d3mD6GDm4AoXD-BbB3kWRsz-hSeybg" , "coinbase: eyJnYXMiOjAsInJlc3VsdCI6eyJzdGF0dXMiOiJzdWNjZXNzIiwiZGF0YSI6Ik5PX0NPSU5CQVNFIn0sInJlcUtleSI6IkRsZFJ3Q2JsUTdMb3F5NndZSm5hb2RIbDMwZDNqM2VILXF0RnpmRXY0NmciLCJsb2dzIjpudWxsLCJtZXRhRGF0YSI6bnVsbCwiY29udGludWF0aW9uIjpudWxsLCJ0eElkIjpudWxsfQ" , "" ] diff --git a/tools/ea/Ea/Genesis.hs b/tools/ea/Ea/Genesis.hs index 1dd6b8e3c3..167d2758ae 100644 --- a/tools/ea/Ea/Genesis.hs +++ b/tools/ea/Ea/Genesis.hs @@ -187,7 +187,7 @@ fastDevelopment0 = Genesis , _coinbase = Just dev0Grants , _keysets = Just devKeysets , _allocations = Just devAllocations - , _namespaces = Just devNs + , _namespaces = Just devNs2 , _coinContract = [fungibleAssetV1, fungibleXChainV1, fungibleAssetV2, installCoinContractV5, gasPayer] } @@ -196,8 +196,11 @@ fastDevelopmentN = fastDevelopment0 & txChainIds .~ ChainIdRange 1 19 & coinbase .~ (Just devNGrants) +devNs2 :: FilePath +devNs2 = "pact/genesis/ns-v2.yaml" + devNs :: FilePath -devNs = "pact/genesis/ns.yaml" +devNs = "pact/genesis/ns-v1.yaml" devKeysets :: FilePath devKeysets = "pact/genesis/devnet/keysets.yaml" @@ -235,7 +238,7 @@ fastTimedCPMN = fastTimedCPM0 & coinbase .~ (Just fastNGrants) fastNs :: FilePath -fastNs = "pact/genesis/ns.yaml" +fastNs = "pact/genesis/ns-v1.yaml" fastKeysets :: FilePath fastKeysets = "pact/genesis/devnet/keysets.yaml" @@ -276,7 +279,7 @@ testNGrants :: FilePath testNGrants = "pact/genesis/testnet/grantsN.yaml" testNs :: FilePath -testNs = "pact/genesis/ns.yaml" +testNs = "pact/genesis/ns-v1.yaml" testnetAllocations :: FilePath testnetAllocations = "pact/genesis/testnet/allocations.yaml" From f28a0ef8ae34de70eacf8a4ce7e99130fa4eb7b1 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 14 Apr 2023 12:20:06 -0400 Subject: [PATCH 58/91] Delete extra bang pattern in CreateProof --- src/Chainweb/SPV/CreateProof.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/SPV/CreateProof.hs b/src/Chainweb/SPV/CreateProof.hs index 6ed475c041..57b8ce6660 100644 --- a/src/Chainweb/SPV/CreateProof.hs +++ b/src/Chainweb/SPV/CreateProof.hs @@ -249,7 +249,7 @@ outputProofPrefix i db payload = do -- 1. TX proof Just outs <- tableLookup blockOutputTable $ _blockPayloadOutputsHash payload -- TODO: use the transaction tree cache - let !(!subj, pos, t) = bodyTree @_ @ChainwebHashTag outs i + let (!subj, pos, t) = bodyTree @_ @ChainwebHashTag outs i -- FIXME use log let tree = (pos, t) -- we blindly trust the ix From 3396134b92069d6724d19b4107fff098f6e39a91 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 17 Apr 2023 10:45:59 -0400 Subject: [PATCH 59/91] Fix verifyTransactionOutputProofat_ --- src/Chainweb/SPV/VerifyProof.hs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Chainweb/SPV/VerifyProof.hs b/src/Chainweb/SPV/VerifyProof.hs index 39ae2977a3..64310675b7 100644 --- a/src/Chainweb/SPV/VerifyProof.hs +++ b/src/Chainweb/SPV/VerifyProof.hs @@ -170,9 +170,12 @@ verifyTransactionOutputProofAt_ -> TransactionOutputProof SHA512t_256 -> BlockHash -> IO TransactionOutput -verifyTransactionOutputProofAt_ bdb proof@(TransactionOutputProof _cid p) ctx = do - unlessM (ancestorOf bdb h ctx) $ throwM - $ SpvExceptionVerificationFailed "target header is not in the chain" +verifyTransactionOutputProofAt_ bdb proof@(TransactionOutputProof tgt p) ctx = do + case tgt of + ProofTargetChain _cid -> + unlessM (ancestorOf bdb h ctx) $ throwM + $ SpvExceptionVerificationFailed "target header is not in the chain" + ProofTargetCrossNetwork _net -> return () proofSubject p where h = runTransactionOutputProof proof From b8952a43800adbc1c37832185cebec75e72f4c9d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 17 Apr 2023 11:15:02 -0400 Subject: [PATCH 60/91] Fix target chain encoding, change pact pin (stop checking yield provenance) --- cabal.project | 2 +- src/Chainweb/SPV.hs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cabal.project b/cabal.project index 49723525c9..b139572abf 100644 --- a/cabal.project +++ b/cabal.project @@ -54,7 +54,7 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 842fbc4256b3cbbde337dbeaa393b649a26f1574 + tag: 1484c15af7f3514524e5986f061f950c030d34b6 source-repository-package type: git diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index ef6efa0399..8079f8e4fd 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -198,9 +198,9 @@ data ProofTarget = ProofTargetChain !ChainId | ProofTargetCrossNetwork !Text deriving anyclass NFData instance ToJSON ProofTarget where - toJSON (ProofTargetChain cid) = toJSON cid + toJSON (ProofTargetChain cid) = toJSON (toText cid) toJSON (ProofTargetCrossNetwork subtgt) = toJSON ("crossnet:" <> subtgt) - toEncoding (ProofTargetChain cid) = toEncoding cid + toEncoding (ProofTargetChain cid) = toEncoding (toText cid) toEncoding (ProofTargetCrossNetwork subtgt) = toEncoding ("crossnet:" <> subtgt) instance FromJSON ProofTarget where From ecc046312132bc8ec109c7e7b8153b1c4436b40f Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Wed, 19 Apr 2023 10:09:07 -0400 Subject: [PATCH 61/91] Disable max block gas limit in fast devnet --- src/Chainweb/Version/FastDevelopment.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/Version/FastDevelopment.hs b/src/Chainweb/Version/FastDevelopment.hs index 78015f4ad7..78880e7153 100644 --- a/src/Chainweb/Version/FastDevelopment.hs +++ b/src/Chainweb/Version/FastDevelopment.hs @@ -49,7 +49,7 @@ fastDevnet = ChainwebVersion ] } - , _versionMaxBlockGasLimit = End (Just 180_000) + , _versionMaxBlockGasLimit = End Nothing , _versionCheats = VersionCheats { _disablePow = True , _fakeFirstEpochStart = True From 0ee5acf7a68a77c872b23412ad00fb5bab3c32af Mon Sep 17 00:00:00 2001 From: chessai Date: Wed, 2 Aug 2023 11:44:25 -0500 Subject: [PATCH 62/91] update pact pin --- cabal.project | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cabal.project b/cabal.project index 8c2f8bcae3..f1700d4c1c 100644 --- a/cabal.project +++ b/cabal.project @@ -59,8 +59,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 0d799a839eca103edecfaca96b82d455e03a2978 - --sha256: 0mjp2fx3bdf3iad87k4sphs8dwfy7qfa10k4lw4sfmah4mc6c3pr + tag: 1bbfc0e8061b1aa9146322e140570f8757a876d8 + --sha256: 0rfzw4kh6ll8hmynpdp284i9sr26vxxi1my6r46y3sad8l27yrlp source-repository-package type: git @@ -141,14 +141,14 @@ allow-newer: servant:* -- these are more liberal than necessary, but since everything works fine -- with this there's no reason to constrain it more than necessary. --- These packages are tightly bound to the GHC version and these +-- These packages are tightly bound to the GHC version and these -- settings ensure that we use the versions that are shipped with the -- GHC version that we are using. allow-newer: *:base allow-newer: *:tempalte-haskell allow-newer: *:ghc-prim --- Pact uses a vendored version of trifecta that has outdated +-- Pact uses a vendored version of trifecta that has outdated -- upper bounds allow-newer: trifecta:* From 36942c31f36e88883596bd3400b67b7ece19cc55 Mon Sep 17 00:00:00 2001 From: chessai Date: Wed, 2 Aug 2023 12:27:11 -0500 Subject: [PATCH 63/91] fix some compilation errors --- src/Chainweb/Cut/CutHashes.hs | 4 ++-- src/Chainweb/Difficulty.hs | 1 - src/Chainweb/Pact/SPV.hs | 4 ++-- src/Chainweb/SPV.hs | 6 ++++++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Chainweb/Cut/CutHashes.hs b/src/Chainweb/Cut/CutHashes.hs index 5d77feafea..2826098575 100644 --- a/src/Chainweb/Cut/CutHashes.hs +++ b/src/Chainweb/Cut/CutHashes.hs @@ -44,7 +44,7 @@ module Chainweb.Cut.CutHashes , BlockHashWithHeight(..) , CutHashes(..) , cutHashes -, cutHashesChainwebVersionName +, cutHashesChainwebVersion , cutHashesId , cutOrigin , cutHashesWeight @@ -359,7 +359,7 @@ cutToCutHashes p c = CutHashes , _cutOrigin = p , _cutHashesWeight = _cutWeight c , _cutHashesHeight = _cutHeight c - , _cutHashesChainwebVersionName = _versionName $ _chainwebVersion c + , _cutHashesChainwebVersion = _chainwebVersion c , _cutHashesId = _cutId c , _cutHashesHeaders = mempty , _cutHashesPayloads = mempty diff --git a/src/Chainweb/Difficulty.hs b/src/Chainweb/Difficulty.hs index cceccb83c2..72345ffa78 100644 --- a/src/Chainweb/Difficulty.hs +++ b/src/Chainweb/Difficulty.hs @@ -89,7 +89,6 @@ import Chainweb.Utils import Chainweb.Utils.Serialization import Numeric.Additive -import Numeric.Natural -- -------------------------------------------------------------------------- -- -- Large Word Orphans diff --git a/src/Chainweb/Pact/SPV.hs b/src/Chainweb/Pact/SPV.hs index 72446cac73..e44d593f61 100644 --- a/src/Chainweb/Pact/SPV.hs +++ b/src/Chainweb/Pact/SPV.hs @@ -143,7 +143,7 @@ verifySPV bdb bh typ proof = runExceptT $ go typ proof -- Chainweb tx output proof "TXOUT" -> do u <- except $ extractProof enableBridge o - unless (view outputProofChainId u == cid) $ + unless (view outputProofChainId u == Just cid) $ forkedThrower bh "cannot redeem spv proof on wrong target chain" -- SPV proof verification is a 3 step process: @@ -187,7 +187,7 @@ verifyCont bdb bh (ContProof cp) = runExceptT $ do case decodeStrict' t of Nothing -> forkedThrower bh "unable to decode continuation proof" Just u - | view outputProofChainId u /= cid -> + | view outputProofChainId u /= Just cid -> forkedThrower bh "cannot redeem continuation proof on wrong target chain" | otherwise -> do diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index 8079f8e4fd..63f55d8b2d 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -27,6 +27,7 @@ module Chainweb.SPV , proofChainId , TransactionOutputProof(..) , outputProofTarget +, outputProofChainId ) where import Control.Applicative @@ -236,3 +237,8 @@ instance FromJSON (TransactionOutputProof SHA512t_256) where -- outputProofTarget :: Getter (TransactionOutputProof a) ProofTarget outputProofTarget = to (\(TransactionOutputProof tgt _) -> tgt) + +outputProofChainId :: Getter (TransactionOutputProof a) (Maybe ChainId) +outputProofChainId = to $ \(TransactionOutputProof tgt _) -> case tgt of + ProofTargetChain cid -> Just cid + ProofTargetCrossNetwork _ -> Nothing From 96ddc3de2da4826b7ad7df5be02b09c2df3d79e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enis=20Bayramo=C4=9Flu?= Date: Wed, 9 Aug 2023 00:26:18 +0200 Subject: [PATCH 64/91] Fix type error in Pact.RestAPI.Server --- src/Chainweb/Pact/RestAPI/Server.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index cbc076d5d0..796f16c7ad 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -463,7 +463,7 @@ spvHandler l cdb cid (SpvRequest rk (Pact.ChainId ptid)) = do toErr $ "SPV verification failed: " <> _spvExceptionMsg e Left e -> toErr $ "Internal error: SPV verification failed: " <> _spvExceptionMsg e - Right q -> return q + Right q -> return $! b64 q where pe = _webPactExecutionService $ view CutDB.cutDbPactService cdb From 9e673cc5cb7cca625fd932990d827f12cb9676df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enis=20Bayramo=C4=9Flu?= Date: Wed, 9 Aug 2023 00:29:27 +0200 Subject: [PATCH 65/91] Remove duplicate defs in Chainweb.Test.Utils --- test/Chainweb/Test/Utils.hs | 47 ------------------------------------- 1 file changed, 47 deletions(-) diff --git a/test/Chainweb/Test/Utils.hs b/test/Chainweb/Test/Utils.hs index 8890c820a1..c67f6419c5 100644 --- a/test/Chainweb/Test/Utils.hs +++ b/test/Chainweb/Test/Utils.hs @@ -999,53 +999,6 @@ withNodes = withNodes_ (genericLogger Error (error . T.unpack)) -- anything. A message at log level error means that the test harness itself -- failed and with thus abort the test. -withNodesAtLatestBehavior - :: ChainwebVersion - -> B.ByteString - -> RocksDb - -> Natural - -> (IO ChainwebNetwork -> TestTree) - -> TestTree -withNodesAtLatestBehavior v testLabel rdb n f = withNodes v testLabel rdb n $ \net -> - withResource (awaitBlockHeight v putStrLn (_getClientEnv <$> net) (latestBehaviorAt v)) (const (return ())) $ \_ -> - f net - --- | Network initialization takes some time. Within my ghci session it took --- about 10 seconds. Once initialization is complete even large numbers of empty --- blocks were mined almost instantaneously. --- -awaitBlockHeight - :: ChainwebVersion - -> (String -> IO ()) - -> IO ClientEnv - -> BlockHeight - -> IO () -awaitBlockHeight v step cenvIo i = do - cenv <- cenvIo - result <- retrying testRetryPolicy checkRetry - $ const $ runClientM (cutGetClient v) cenv - case result of - Left e -> throwM e - Right x - | all (\bh -> _bhwhHeight bh >= i) (_cutHashes x) -> return () - | otherwise -> error - $ "retries exhausted: waiting for cut height " <> sshow i - <> " but only got " <> sshow (_cutHashesHeight x) - where - checkRetry s (Left e) = do - step $ "awaiting cut of height " <> show i - <> ". No result from node: " <> show e - <> " [" <> show (view rsIterNumberL s) <> "]" - return True - checkRetry s (Right c) - | all (\bh -> _bhwhHeight bh >= i) (_cutHashes c) = return False - | otherwise = do - step - $ "awaiting cut with all block heights >= " <> show i - <> ". Current cut height: " <> show (_cutHashesHeight c) - <> ". Current block heights: " <> show (_bhwhHeight <$> _cutHashes c) - <> " [" <> show (view rsIterNumberL s) <> "]" - return True withNodesAtLatestBehavior :: ChainwebVersion From d3ffd0cbb495a965de1bb019cd99fb601640148e Mon Sep 17 00:00:00 2001 From: chessai Date: Wed, 16 Aug 2023 12:39:22 -0500 Subject: [PATCH 66/91] update pact pin --- cabal.project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 482db8423c..7d0b668c13 100644 --- a/cabal.project +++ b/cabal.project @@ -59,8 +59,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 1bbfc0e8061b1aa9146322e140570f8757a876d8 - --sha256: 0rfzw4kh6ll8hmynpdp284i9sr26vxxi1my6r46y3sad8l27yrlp + tag: a9380405a4769e8ec98b4540313fce8991c0f777 + --sha256: 0mga55x9hvnxjss6ci5n9z0pmdq1jkrl6ijrd8yaigmvv8jfjvm3 source-repository-package type: git From 73d677073f3e654e65d4e85fb988e8eda5bf9058 Mon Sep 17 00:00:00 2001 From: chessai Date: Wed, 16 Aug 2023 12:57:05 -0500 Subject: [PATCH 67/91] fix output proof checking in SPV and crossnet --- src/Chainweb/Pact/SPV.hs | 6 ++---- src/Chainweb/SPV.hs | 6 ------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Chainweb/Pact/SPV.hs b/src/Chainweb/Pact/SPV.hs index e44d593f61..a991dce110 100644 --- a/src/Chainweb/Pact/SPV.hs +++ b/src/Chainweb/Pact/SPV.hs @@ -143,7 +143,7 @@ verifySPV bdb bh typ proof = runExceptT $ go typ proof -- Chainweb tx output proof "TXOUT" -> do u <- except $ extractProof enableBridge o - unless (view outputProofChainId u == Just cid) $ + unless (view outputProofTarget u == ProofTargetChain cid) $ forkedThrower bh "cannot redeem spv proof on wrong target chain" -- SPV proof verification is a 3 step process: @@ -169,8 +169,6 @@ verifySPV bdb bh typ proof = runExceptT $ go typ proof t -> throwError $! "unsupported SPV types: " <> t - - -- | SPV defpact transaction verification support. This call validates a pact 'endorsement' -- in Pact, providing a validation that the yield data of a cross-chain pact is valid. -- @@ -187,7 +185,7 @@ verifyCont bdb bh (ContProof cp) = runExceptT $ do case decodeStrict' t of Nothing -> forkedThrower bh "unable to decode continuation proof" Just u - | view outputProofChainId u /= Just cid -> + | ProofTargetChain tcid <- view outputProofTarget u, tcid /= cid -> forkedThrower bh "cannot redeem continuation proof on wrong target chain" | otherwise -> do diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index 63f55d8b2d..8079f8e4fd 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -27,7 +27,6 @@ module Chainweb.SPV , proofChainId , TransactionOutputProof(..) , outputProofTarget -, outputProofChainId ) where import Control.Applicative @@ -237,8 +236,3 @@ instance FromJSON (TransactionOutputProof SHA512t_256) where -- outputProofTarget :: Getter (TransactionOutputProof a) ProofTarget outputProofTarget = to (\(TransactionOutputProof tgt _) -> tgt) - -outputProofChainId :: Getter (TransactionOutputProof a) (Maybe ChainId) -outputProofChainId = to $ \(TransactionOutputProof tgt _) -> case tgt of - ProofTargetChain cid -> Just cid - ProofTargetCrossNetwork _ -> Nothing From b729fcdc643c40db7821bd116c47c733408513b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enis=20Bayramo=C4=9Flu?= Date: Thu, 31 Aug 2023 16:35:57 +0200 Subject: [PATCH 68/91] Pin latest pact/edmund/l2-spv-poc --- cabal.project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index e8ac6419eb..39704984b7 100644 --- a/cabal.project +++ b/cabal.project @@ -59,8 +59,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 8c47f3dfb9070312426e35581b58ac5f66a85c16 - --sha256: 0k6s7xbgi019bw32xz773c36bx54zymbgvrvrj5sjrp3fzrd74pr + tag: 4e04f83ee2b185cbc48727fe57b4ddabd20e2f0f + --sha256: 006m8idx88rm5i5a1s8qw1ls8ynj63r4sf4jxz3abg1f7h7sxxcf source-repository-package type: git From 5fd6c955267d3f202ffa7257c40bbf1478179e67 Mon Sep 17 00:00:00 2001 From: Andy Tang Date: Tue, 24 Oct 2023 09:10:44 +0200 Subject: [PATCH 69/91] Disable keyset format validation --- src/Chainweb/Pact/TransactionExec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index cbc1dbec0d..059c973713 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -344,7 +344,7 @@ flagsFor v cid bh = S.fromList $ concat [ enablePactEvents' v cid bh , enablePact40 v cid bh , enablePact420 v cid bh - , enforceKeysetFormats' v cid bh + -- , enforceKeysetFormats' v cid bh , enablePactModuleMemcheck v cid bh , enablePact43 v cid bh , enablePact431 v cid bh From b63156bae396ac2d3b8bba7d3cea4978f6c72bb3 Mon Sep 17 00:00:00 2001 From: Andy Tang Date: Tue, 24 Oct 2023 09:55:11 +0200 Subject: [PATCH 70/91] Some yolo fix --- src/Chainweb/SPV.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Chainweb/SPV.hs b/src/Chainweb/SPV.hs index 1a1026e1bd..a7ca5c0cb9 100644 --- a/src/Chainweb/SPV.hs +++ b/src/Chainweb/SPV.hs @@ -93,8 +93,8 @@ instance Exception SpvException -- legacy format for existing endpoints in order to not break existing clients. -- proofProperties - :: forall kv - . KeyValue kv + :: forall kv e + . KeyValue e kv => ProofTarget -> MerkleProof SHA512t_256 -> [kv] From 1426b9200a30c57f1c9781b19b4b70431b457afb Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Wed, 8 Nov 2023 11:02:59 -0800 Subject: [PATCH 71/91] wip command verifier --- cabal.project | 4 ++-- src/Chainweb/Pact/Validations.hs | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 39554ed52e..40fca2e2d4 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 9d8d0975d58fd811404130a57724bfb5a2f2af80 - --sha256: sha256-4oVJnV12kXraWRQjJJZv3wuoEGfGVFC1EIkzYJrUVs8= + tag: a80fa5036d2399bcc92472f5d3e7606a9aba62b0 + --sha256: sha256-M8Oaeh0IZr0kKkjq0pBwrAbFtRU7oxQpl20S0XcShJk= source-repository-package type: git diff --git a/src/Chainweb/Pact/Validations.hs b/src/Chainweb/Pact/Validations.hs index d893867255..f59500323d 100644 --- a/src/Chainweb/Pact/Validations.hs +++ b/src/Chainweb/Pact/Validations.hs @@ -186,6 +186,16 @@ assertTxTimeRelativeToParent (ParentCreationTime (BlockCreationTime txValidation P.TxCreationTime txOriginationTime = view cmdCreationTime tx lenientTxValidationTime = add (scaleTimeSpan defaultLenientTimeSlop second) txValidationTime +-- | Assert that the command hash matches its payload and +-- its signatures are valid, without parsing the payload. +assertCommand :: P.Command PayloadWithText -> [P.PPKScheme] -> Bool +assertCommand cmd@(P.Command (PayloadWithText { _payloadBytes, _payloadObj }) sigs hsh) ppkSchemePassList = + assertHash && + assertValidateSigs ppkSchemePassList hsh signers sigs + where + signers = P._pSigners p + assertHash = Pact.verifyHash @'Pact.Blake2b_256 (_cmdHash cmdBS) (_cmdPayload cmdBS) + -- -------------------------------------------------------------------- -- -- defaults From d970619a722293cdf26839d3ffdfc42c92fb0827 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Wed, 8 Nov 2023 12:19:56 -0800 Subject: [PATCH 72/91] Thread KeysetPublicKey through chainweb --- cabal.project | 4 ++-- src/Chainweb/Miner/Config.hs | 7 +++---- src/Chainweb/Miner/Pact.hs | 4 ++-- src/Chainweb/Pact/Utils.hs | 22 ++++++++++++++-------- src/Chainweb/Pact/Validations.hs | 15 ++++++++++----- src/Chainweb/Rosetta/Utils.hs | 12 ++++++++++-- src/Chainweb/Transaction.hs | 16 ++++++++-------- 7 files changed, 49 insertions(+), 31 deletions(-) diff --git a/cabal.project b/cabal.project index 40fca2e2d4..0298cc8d21 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: a80fa5036d2399bcc92472f5d3e7606a9aba62b0 - --sha256: sha256-M8Oaeh0IZr0kKkjq0pBwrAbFtRU7oxQpl20S0XcShJk= + tag: 5d54ff741c8233b1ff9f7a38f763be17b7ba19ab + --sha256: sha256-VubTLojYnM7PJ1Y4Du+IkCypVQKlNHUH9RKfcpo6vUQ= source-repository-package type: git diff --git a/src/Chainweb/Miner/Config.hs b/src/Chainweb/Miner/Config.hs index 520783c1a6..ff068c85f2 100644 --- a/src/Chainweb/Miner/Config.hs +++ b/src/Chainweb/Miner/Config.hs @@ -50,7 +50,7 @@ import Numeric.Natural (Natural) import Options.Applicative import qualified Pact.JSON.Encode as J -import Pact.Types.Term (mkKeySet, PublicKeyText(..)) +import Pact.Types.Term (mkKeySetText, PublicKeyText(..)) -- internal modules @@ -204,7 +204,7 @@ pMiner prefix = pkToMiner <$> pPk where pkToMiner pk = Miner (MinerId $ "k:" <> _pubKey pk) - (MinerKeys $ mkKeySet [pk] "keys-all") + (MinerKeys $ mkKeySetText [pk] "keys-all") pPk = strOption % long (prefix <> "mining-public-key") <> help "public key of a miner in hex decimal encoding. The account name is the public key prefix by 'k:'. (This option can be provided multiple times.)" @@ -258,5 +258,4 @@ defaultNodeMining = NodeMiningConfig } invalidMiner :: Miner -invalidMiner = Miner "" . MinerKeys $ mkKeySet [] "keys-all" - +invalidMiner = Miner "" . MinerKeys $ mkKeySetText [] "keys-all" diff --git a/src/Chainweb/Miner/Pact.hs b/src/Chainweb/Miner/Pact.hs index fbfc55ddd9..2efb8887be 100644 --- a/src/Chainweb/Miner/Pact.hs +++ b/src/Chainweb/Miner/Pact.hs @@ -62,7 +62,7 @@ import Chainweb.Payload import Chainweb.Utils import qualified Pact.JSON.Encode as J -import Pact.Types.Term (KeySet(..), mkKeySet) +import Pact.Types.Term (KeySet(..), mkKeySet, mkKeySetText) -- -------------------------------------------------------------------------- -- -- Miner data @@ -125,7 +125,7 @@ minerKeys = lens (\(Miner _ k) -> k) (\(Miner i _) b -> Miner i b) defaultMiner :: Miner defaultMiner = Miner (MinerId "miner") $ MinerKeys - $ mkKeySet + $ mkKeySetText ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "keys-all" diff --git a/src/Chainweb/Pact/Utils.hs b/src/Chainweb/Pact/Utils.hs index 90930fc273..3e2869d3df 100644 --- a/src/Chainweb/Pact/Utils.hs +++ b/src/Chainweb/Pact/Utils.hs @@ -17,7 +17,6 @@ module Chainweb.Pact.Utils , toTxCreationTime -- * k:account helper functions - , validatePubKey , validateKAccount , extractPubKeyFromKAccount , generateKAccountFromPubKey @@ -38,7 +37,8 @@ import Pact.Parse import qualified Pact.Types.ChainId as P import qualified Pact.Types.Term as P import Pact.Types.ChainMeta -import Pact.Types.KeySet (validateKeyFormat) +import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), ed25519HexFormat) +import Pact.Types.Crypto (PPKScheme(ED25519)) import qualified Pact.JSON.Encode as J @@ -64,15 +64,16 @@ toTxCreationTime (Time timespan) = TxCreationTime $ ParsedInteger $ fromIntegral $ timeSpanToSeconds timespan -validatePubKey :: P.PublicKeyText -> Bool -validatePubKey = validateKeyFormat +-- TODO: This will fail for k: accounts that correspond to WebAuthn public +-- keys. Consider extending it when we integrate WebAuthn with k: accounts. +-- Note: This function is only used in Rosetta as a validation step. validateKAccount :: T.Text -> Bool validateKAccount acctName = case T.take 2 acctName of "k:" -> let pubKey = P.PublicKeyText $ T.drop 2 acctName - in validateKeyFormat pubKey + in ed25519HexFormat $ KeysetPublicKey pubKey ED25519 _ -> False extractPubKeyFromKAccount :: T.Text -> Maybe P.PublicKeyText @@ -83,15 +84,20 @@ extractPubKeyFromKAccount kacct generateKAccountFromPubKey :: P.PublicKeyText -> Maybe T.Text generateKAccountFromPubKey pubKey - | validatePubKey pubKey = + | validPubKey = let pubKeyText = P._pubKey pubKey in Just $ "k:" <> pubKeyText | otherwise = Nothing + where + validPubKey :: Bool + validPubKey = ed25519HexFormat $ KeysetPublicKey pubKey ED25519 + -- Warning: Only use if already certain that PublicKeyText -- is valid. +-- Note: We are assuming the k: account is ED25519. pubKeyToKAccountKeySet :: P.PublicKeyText -> P.KeySet -pubKeyToKAccountKeySet pubKey = P.mkKeySet [pubKey] "keys-all" +pubKeyToKAccountKeySet pubKey = P.mkKeySetText [pubKey] "keys-all" generateKeySetFromKAccount :: T.Text -> Maybe P.KeySet generateKeySetFromKAccount kacct = do @@ -112,4 +118,4 @@ emptyPayload :: PayloadWithOutputs emptyPayload = newPayloadWithOutputs miner coinbase mempty where miner = MinerData $ J.encodeStrict noMiner - coinbase = noCoinbaseOutput \ No newline at end of file + coinbase = noCoinbaseOutput diff --git a/src/Chainweb/Pact/Validations.hs b/src/Chainweb/Pact/Validations.hs index f59500323d..08c3b9a145 100644 --- a/src/Chainweb/Pact/Validations.hs +++ b/src/Chainweb/Pact/Validations.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} -- | @@ -24,6 +25,7 @@ module Chainweb.Pact.Validations , assertTxSize , assertValidateSigs , assertTxTimeRelativeToParent +, assertCommand -- * Defaults , defaultMaxCommandUserSigListSize , defaultMaxCoinDecimalPlaces @@ -35,8 +37,10 @@ import Control.Lens import Data.Decimal (decimalPlaces) import Data.Maybe (isJust, catMaybes, fromMaybe) +import Data.Either (isRight) import Data.List.NonEmpty (NonEmpty, nonEmpty) import Data.Text (Text) +import qualified Data.ByteString.Short as SBS import Data.Word (Word8) -- internal modules @@ -47,7 +51,7 @@ import Chainweb.Pact.Types import Chainweb.Pact.Utils (fromPactChainId) import Chainweb.Pact.Service.Types import Chainweb.Time (Seconds(..), Time(..), secondsToTimeSpan, scaleTimeSpan, second, add) -import Chainweb.Transaction (cmdTimeToLive, cmdCreationTime) +import Chainweb.Transaction (cmdTimeToLive, cmdCreationTime, PayloadWithText, payloadBytes, payloadObj) import Chainweb.Version import Chainweb.Version.Guards (validPPKSchemes) @@ -189,12 +193,13 @@ assertTxTimeRelativeToParent (ParentCreationTime (BlockCreationTime txValidation -- | Assert that the command hash matches its payload and -- its signatures are valid, without parsing the payload. assertCommand :: P.Command PayloadWithText -> [P.PPKScheme] -> Bool -assertCommand cmd@(P.Command (PayloadWithText { _payloadBytes, _payloadObj }) sigs hsh) ppkSchemePassList = - assertHash && +assertCommand (P.Command pwt sigs hsh) ppkSchemePassList = + isRight assertHash && assertValidateSigs ppkSchemePassList hsh signers sigs where - signers = P._pSigners p - assertHash = Pact.verifyHash @'Pact.Blake2b_256 (_cmdHash cmdBS) (_cmdPayload cmdBS) + cmdBS = SBS.fromShort $ payloadBytes pwt + signers = P._pSigners (payloadObj pwt) + assertHash = P.verifyHash @'P.Blake2b_256 hsh cmdBS -- -------------------------------------------------------------------- -- -- defaults diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index f62369a809..111e059326 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -22,6 +22,7 @@ import Data.Foldable (foldl') import Data.Decimal ( Decimal, DecimalRaw(Decimal) ) import Data.Hashable (Hashable(..)) import Data.List (sortOn, inits) +import Data.Maybe (mapMaybe) import Data.Word (Word32, Word64) import Text.Read (readMaybe) import Text.Printf ( printf ) @@ -45,6 +46,7 @@ import Numeric.Natural ( Natural ) import Pact.Types.Command import Pact.Types.PactValue (PactValue(..)) +import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), PublicKeyText(..)) import Pact.Types.Exp (Literal(..)) import Pact.JSON.Legacy.Value @@ -762,6 +764,8 @@ getCmdPayload (Command p _ _) = (decodeStrict' $! T.encodeUtf8 p) +-- TODO: This assumes Rosettas signatures are all Ed25519 signatures +-- (Not webauthn). matchSigs :: [RosettaSignature] -> [Signer] @@ -789,7 +793,7 @@ matchSigs sigs signers = do (Left $ stringRosettaError RosettaInvalidSignature $ "Expected the same Signature and PublicKey type for Signature=" ++ show sig) - let userSig = P.UserSig sig + let userSig = P.ED25519Sig sig addr <- toPactPubKeyAddr pk pure (addr, userSig) @@ -1178,7 +1182,11 @@ toRosettaError failure = annotate (stringRosettaError failure) ksToPubKeys :: P.KeySet -> [T.Text] ksToPubKeys (P.KeySet pkSet _) = - map P._pubKey (S.toList pkSet) + mapMaybe (\(KeysetPublicKey (PublicKeyText pk) sch) -> + if sch == ED25519 + then Just pk + else Nothing + ) (S.toList pkSet) parsePubKeys :: T.Text -> Value -> Either RosettaError [T.Text] diff --git a/src/Chainweb/Transaction.hs b/src/Chainweb/Transaction.hs index 64c9ba345a..a5fa574e26 100644 --- a/src/Chainweb/Transaction.hs +++ b/src/Chainweb/Transaction.hs @@ -32,7 +32,7 @@ import Control.Lens import qualified Data.Aeson as Aeson import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B -import qualified Data.ByteString.Short as SB +import qualified Data.ByteString.Short as SBS import Data.Hashable import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) @@ -54,13 +54,13 @@ import Chainweb.Utils.Serialization -- the Text that generated it, to make gossiping easier. -- data PayloadWithText = PayloadWithText - { _payloadBytes :: !SB.ShortByteString + { _payloadBytes :: !SBS.ShortByteString , _payloadObj :: !(Payload PublicMeta ParsedCode) } deriving (Show, Eq, Generic) deriving anyclass (NFData) -payloadBytes :: PayloadWithText -> SB.ShortByteString +payloadBytes :: PayloadWithText -> SBS.ShortByteString payloadBytes = _payloadBytes payloadObj :: PayloadWithText -> Payload PublicMeta ParsedCode @@ -68,13 +68,13 @@ payloadObj = _payloadObj mkPayloadWithText :: Command ByteString -> Payload PublicMeta ParsedCode -> PayloadWithText mkPayloadWithText cmd p = PayloadWithText - { _payloadBytes = SB.toShort $ _cmdPayload cmd + { _payloadBytes = SBS.toShort $ _cmdPayload cmd , _payloadObj = p } mkPayloadWithTextOld :: Payload PublicMeta ParsedCode -> PayloadWithText mkPayloadWithTextOld p = PayloadWithText - { _payloadBytes = SB.toShort $ J.encodeStrict $ toLegacyJsonViaEncode $ fmap _pcCode p + { _payloadBytes = SBS.toShort $ J.encodeStrict $ toLegacyJsonViaEncode $ fmap _pcCode p , _payloadObj = p } @@ -94,7 +94,7 @@ instance Hashable (HashableTrans PayloadWithText) where where (TypedHash hc) = _cmdHash t decHC = runGetEitherS getWord64le - !hashCode = either error id $ decHC (B.take 8 $ SB.fromShort hc) + !hashCode = either error id $ decHC (B.take 8 $ SBS.fromShort hc) {-# INLINE hashWithSalt #-} -- | A codec for (Command PayloadWithText) transactions. @@ -110,7 +110,7 @@ chainwebPayloadCodec ppv = Codec enc dec Nothing -> Left "decode PayloadWithText failed" encodePayload :: PayloadWithText -> ByteString -encodePayload = SB.fromShort . _payloadBytes +encodePayload = SBS.fromShort . _payloadBytes decodePayload :: PactParserVersion @@ -119,7 +119,7 @@ decodePayload decodePayload ppv bs = case Aeson.decodeStrict' bs of Just payload -> do p <- traverse (parsePact ppv) payload - return $! PayloadWithText (SB.toShort bs) p + return $! PayloadWithText (SBS.toShort bs) p Nothing -> Left "decoding Payload failed" parsePact From 4bd97c1e8c8c4f856471a9c190c0c2c660322816 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Wed, 8 Nov 2023 12:27:37 -0800 Subject: [PATCH 73/91] Migrate to KeysetPublicKey in tests --- test/Chainweb/Test/Pact/Checkpointer.hs | 2 +- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 4 ++-- test/Chainweb/Test/Pact/RemotePactTest.hs | 2 +- test/Chainweb/Test/Pact/SPV.hs | 2 +- test/Chainweb/Test/Pact/TransactionTests.hs | 4 ++-- test/Chainweb/Test/Pact/Utils.hs | 2 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/Chainweb/Test/Pact/Checkpointer.hs b/test/Chainweb/Test/Pact/Checkpointer.hs index 2a4f1d4c11..fa4ca9e189 100644 --- a/test/Chainweb/Test/Pact/Checkpointer.hs +++ b/test/Chainweb/Test/Pact/Checkpointer.hs @@ -505,7 +505,7 @@ runRegression pactdb e schemaInit = do let row' = RowData RDV1 $ ObjectMap $ M.fromList [("gah",toPV False),("fh",toPV (1 :: Int))] _writeRow pactdb Update usert "key1" row' conn assertEquals' "user update" (Just row') (_readRow pactdb usert "key1" conn) - let ks = mkKeySet [PublicKeyText "skdjhfskj"] "predfun" + let ks = mkKeySetText [PublicKeyText "skdjhfskj"] "predfun" _writeRow pactdb Write KeySets "ks1" ks conn assertEquals' "keyset write" (Just ks) $ _readRow pactdb KeySets "ks1" conn (modName,modRef,mod') <- loadModule diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 649df5ad04..ba80fbe5c5 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -165,7 +165,7 @@ minerKeysetTest = do where - badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySet ["bad-bad-bad"] "keys-all" + badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySetText ["bad-bad-bad"] "keys-all" txTimeoutTest :: PactTestM () txTimeoutTest = do @@ -1114,7 +1114,7 @@ pact4coin3UpgradeTest = do , PactTxTest badKeyset $ assertTxSuccess "Should allow bad keys" $ - pKeySet $ mkKeySet ["badkey"] "keys-all" + pKeySet $ mkKeySetText ["badkey"] "keys-all" ] assertTxEvents "Coinbase events @ block 7" [] =<< cbResult diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 192daf5539..895041998d 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -881,7 +881,7 @@ allocationTest t cenv step = do tx3 = let c = "(define-keyset \"allocation02\" (read-keyset \"allocation02-keyset\"))" - d = mkKeySet + d = mkKeySetText ["0c8212a903f6442c84acd0069acc263c69434b5af37b2997b16d6348b53fcd0a"] "keys-all" in PactTransaction c $ Just (A.object [ "allocation02-keyset" A..= J.toJsonViaEncode d ]) diff --git a/test/Chainweb/Test/Pact/SPV.hs b/test/Chainweb/Test/Pact/SPV.hs index d99bfbef07..2c49148e76 100644 --- a/test/Chainweb/Test/Pact/SPV.hs +++ b/test/Chainweb/Test/Pact/SPV.hs @@ -402,7 +402,7 @@ burnGen time pidv sid tid = do tx1Data = -- sender01 keyset guard - let ks = mkKeySet + let ks = mkKeySetText ["6be2f485a7af75fedb4b7f153a903f7e6000ca4aa501179c91a2450b777bd2a7"] "keys-all" diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 126a06c9c5..194bff07df 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -196,7 +196,7 @@ badMinerId :: MinerId badMinerId = MinerId "alpha\" (read-keyset \"miner-keyset\") 9999999.99)(coin.coinbase \"alpha" minerKeys0 :: MinerKeys -minerKeys0 = MinerKeys $ mkKeySet +minerKeys0 = MinerKeys $ mkKeySetText ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "default" @@ -267,7 +267,7 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do miner = Miner (MinerId "tester01\" (read-keyset \"miner-keyset\") 1000.0)(coin.coinbase \"tester01") - (MinerKeys $ mkKeySet ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") + (MinerKeys $ mkKeySetText ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") preForkHeight = 121451 postForkHeight = 121452 diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index abe29dd80f..e7bd554f9e 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -237,7 +237,7 @@ mkKeySetData :: Key -> [SimpleKeyPair] -> Value mkKeySetData name keys = object [ name .= map fst keys ] sender00Ks :: KeySet -sender00Ks = mkKeySet +sender00Ks = mkKeySetText ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] "keys-all" diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 6bb470b4a3..a57a793efc 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -491,7 +491,7 @@ constructionTransferTests _ envIo = ks (TestKeySet _ Nothing pred') = P.mkKeySet [] pred' ks (TestKeySet _ (Just (pk,_)) pred') = - P.mkKeySet [P.PublicKeyText pk] pred' + P.mkKeySetText [P.PublicKeyText pk] pred' sender00KAcct = "k:" <> fst sender00 sender01KAcct = "k:" <> fst sender01 @@ -600,14 +600,14 @@ submitToConstructionAPI expectOps chainId' payer getKeys expectResult cenv step (Right (hsh :: P.PactHash)) <- pure $ fmap (P.fromUntypedHash . P.Hash . BS.toShort) (P.parseB16TextOnly $ _rosettaSigningPayload_hexBytes payload) - sig <- P.signHash hsh kp + ED25519Sig sig <- P.signHash hsh kp pure $! RosettaSignature { _rosettaSignature_signingPayload = payload , _rosettaSignature_publicKey = RosettaPublicKey pk CurveEdwards25519 , _rosettaSignature_signatureType = RosettaEd25519 - , _rosettaSignature_hexBytes = P._usSig sig + , _rosettaSignature_hexBytes = sig } acct n = AccountId n Nothing Nothing From 9bbafb837ec2ce05ddf9fbfacb19eff36bf57eba Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Wed, 8 Nov 2023 14:57:07 -0800 Subject: [PATCH 74/91] Work the validateCommand function with chainid and version through more of chainweb. Next stop: execBlock --- bench/Chainweb/Pact/Backend/ForkingBench.hs | 2 + cabal.project | 4 +- src/Chainweb/Pact/RestAPI/Server.hs | 43 ++++++++++++++------- src/Chainweb/Rosetta/RestAPI/Server.hs | 15 +++---- test/Chainweb/Test/Pact/Utils.hs | 1 + tools/ea/Ea.hs | 1 + 6 files changed, 44 insertions(+), 22 deletions(-) diff --git a/bench/Chainweb/Pact/Backend/ForkingBench.hs b/bench/Chainweb/Pact/Backend/ForkingBench.hs index fc5d8b819b..b86e93653a 100644 --- a/bench/Chainweb/Pact/Backend/ForkingBench.hs +++ b/bench/Chainweb/Pact/Backend/ForkingBench.hs @@ -450,6 +450,8 @@ formatB16PubKey = toB16Text . getPublic safeCapitalize :: String -> String safeCapitalize = maybe [] (uncurry (:) . bimap toUpper (Prelude.map toLower)) . Data.List.uncons + +-- TODO: Use the new `assertCommand` function. validateCommand :: Command Text -> Either String ChainwebTransaction validateCommand cmdText = case verifyCommand cmdBS of ProcSucc cmd -> Right (mkPayloadWithTextOld <$> cmd) diff --git a/cabal.project b/cabal.project index 0298cc8d21..9bf3f1d11f 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 5d54ff741c8233b1ff9f7a38f763be17b7ba19ab - --sha256: sha256-VubTLojYnM7PJ1Y4Du+IkCypVQKlNHUH9RKfcpo6vUQ= + tag: bdbc0043c27c7ca0ea58bf6b034216f163079b57 + --sha256: sha256-U430nKNe3sEowX8yMjF8yLK0IoURakKsVecNnJQ/xAw= source-repository-package type: git diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index f50d014a57..1145cd5572 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -45,7 +45,6 @@ import Control.Monad.Trans.Maybe import Data.Aeson as Aeson import Data.Bifunctor (second) -import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Char8 as BSL8 import qualified Data.ByteString.Short as SB @@ -117,6 +116,8 @@ import Chainweb.Transaction import qualified Chainweb.TreeDB as TreeDB import Chainweb.Utils import Chainweb.Version +import Chainweb.Pact.Validations (assertCommand) +import Chainweb.Version.Guards (validPPKSchemes) import Chainweb.WebPactExecutionService import Chainweb.Storage.Table @@ -125,6 +126,7 @@ import qualified Pact.JSON.Encode as J import qualified Pact.Parse as Pact import Pact.Types.API import qualified Pact.Types.ChainId as Pact +import Pact.Types.ChainMeta (PublicMeta) import Pact.Types.Command import Pact.Types.Hash (Hash(..)) import qualified Pact.Types.Hash as Pact @@ -185,12 +187,13 @@ pactServer d = logger = _pactServerDataLogger d pact = _pactServerDataPact d cdb = _pactServerDataCutDb d + v = _chainwebVersion cdb pactApiHandlers - = sendHandler logger mempool + = sendHandler logger v cid mempool :<|> pollHandler logger cdb cid pact mempool :<|> listenHandler logger cdb cid pact mempool - :<|> localHandler logger pact + :<|> localHandler logger v cid pact pactSpvHandler = spvHandler logger cdb cid pactSpv2Handler = spv2Handler logger cdb cid @@ -246,12 +249,14 @@ instance ToJSON PactCmdLog where sendHandler :: Logger logger => logger + -> ChainwebVersion + -> ChainId -> MempoolBackend ChainwebTransaction -> SubmitBatch -> Handler RequestKeys -sendHandler logger mempool (SubmitBatch cmds) = Handler $ do +sendHandler logger v cid mempool (SubmitBatch cmds) = Handler $ do liftIO $ logg Info (PactCmdLogSend cmds) - case traverse validateCommand cmds of + case traverse (validateCommand v cid) cmds of Right enriched -> do let txs = V.fromList $ NEL.toList enriched -- If any of the txs in the batch fail validation, we reject them all. @@ -364,6 +369,8 @@ listenHandler logger cdb cid pact mem (ListenerRequest key) = do localHandler :: Logger logger => logger + -> ChainwebVersion + -> ChainId -> PactExecutionService -> Maybe LocalPreflightSimulation -- ^ Preflight flag @@ -373,7 +380,7 @@ localHandler -- ^ Rewind depth -> Command Text -> Handler LocalResult -localHandler logger pact preflight sigVerify rewindDepth cmd = do +localHandler logger v cid pact preflight sigVerify rewindDepth cmd = do liftIO $ logg Info $ PactCmdLogLocal cmd cmd' <- case doCommandValidation cmd of Right c -> return c @@ -408,7 +415,7 @@ localHandler logger pact preflight sigVerify rewindDepth cmd = do let cmd' = cmdBS { _cmdPayload = p } pure $ mkPayloadWithText cmdBS <$> cmd' - | otherwise = validateCommand cmd + | otherwise = validateCommand v cid cmd -- -------------------------------------------------------------------------- -- -- Cross Chain SPV Handler @@ -679,13 +686,23 @@ internalPoll pdb bhdb mempool pactEx cut confDepth requestKeys0 = do toPactTx :: Transaction -> Maybe (Command Text) toPactTx (Transaction b) = decodeStrict' b -validateCommand :: Command Text -> Either String ChainwebTransaction -validateCommand cmdText = case verifyCommand cmdBS of - ProcSucc cmd -> Right (mkPayloadWithText cmdBS <$> cmd) - ProcFail err -> Left err + +validateCommand :: ChainwebVersion -> ChainId -> Command Text -> Either String ChainwebTransaction +validateCommand v cid cmdText = case parsedPayload of + Right (parsedPact :: Payload PublicMeta ParsedCode) -> + let pwt = mkPayloadWithText cmdBs parsedPact + commandParsed = cmdText { _cmdPayload = pwt } + in + if assertCommand commandParsed (validPPKSchemes v cid maxBound) + then Right commandParsed + else Left "Command failed validation" + Left e -> Left $ "Pact parsing error: " ++ e + where - cmdBS :: Command ByteString - cmdBS = encodeUtf8 <$> cmdText + payloadBs = encodeUtf8 (_cmdPayload cmdText) + cmdBs = cmdText { _cmdPayload = payloadBs } + parsedPayload = traverse (parsePact (maxBound :: PactParserVersion)) + =<< Aeson.eitherDecodeStrict' payloadBs -- | Validate the length of the request key's underlying hash. diff --git a/src/Chainweb/Rosetta/RestAPI/Server.hs b/src/Chainweb/Rosetta/RestAPI/Server.hs index 52112d7e1b..15ea1e6d33 100644 --- a/src/Chainweb/Rosetta/RestAPI/Server.hs +++ b/src/Chainweb/Rosetta/RestAPI/Server.hs @@ -345,17 +345,18 @@ constructionParseH :: ChainwebVersion -> ConstructionParseReq -> Handler ConstructionParseResp -constructionParseH v (ConstructionParseReq net isSigned tx) = +constructionParseH v (ConstructionParseReq net isSigned tx) = do either throwRosettaError pure work where work :: Either RosettaError ConstructionParseResp work = do + cid <- annotate rosettaError' (validateNetwork v net) void $ annotate rosettaError' (validateNetwork v net) (EnrichedCommand cmd txInfo signAccts) <- note (rosettaError' RosettaUnparsableTx) $ textToEnrichedCommand tx - signers <- getRosettaSigners cmd signAccts + signers <- getRosettaSigners cid cmd signAccts let ops = txToOps txInfo pure $ ConstructionParseResp @@ -365,9 +366,9 @@ constructionParseH v (ConstructionParseReq net isSigned tx) = , _constructionParseResp_metadata = Nothing } - getRosettaSigners cmd expectedSignerAccts + getRosettaSigners cid cmd expectedSignerAccts | isSigned = do - _ <- toRosettaError RosettaInvalidTx $ validateCommand cmd + _ <- toRosettaError RosettaInvalidTx $ validateCommand v cid cmd pure expectedSignerAccts -- If transaction signatures successfully validates, -- it was signed correctly with all of the account public @@ -379,7 +380,7 @@ constructionParseH v (ConstructionParseReq net isSigned tx) = constructionCombineH :: ConstructionCombineReq -> Handler ConstructionCombineResp -constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = +constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = do either throwRosettaError pure work where work :: Either RosettaError ConstructionCombineResp @@ -398,7 +399,7 @@ constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = constructionHashH :: ConstructionHashReq -> Handler TransactionIdResp -constructionHashH (ConstructionHashReq _ signedTx) = +constructionHashH (ConstructionHashReq _ signedTx) = do either throwRosetta pure work where work :: Either RosettaFailure TransactionIdResp @@ -437,7 +438,7 @@ constructionSubmitH v ms (ConstructionSubmitReq net tx) = note (rosettaError' RosettaUnparsableTx) $ textToEnrichedCommand tx - case validateCommand cmd of + case validateCommand v cid cmd of Right validated -> do let txs = V.fromList [validated] -- If any of the txs in the batch fail validation, we reject them all. diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index e7bd554f9e..360bfbd106 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -522,6 +522,7 @@ mkCmd nonce rpc = defaultCmd -- | Build parsed + verified Pact command -- +-- TODO: Use the new `assertCommand` function. buildCwCmd :: (MonadThrow m, MonadIO m) => CmdBuilder -> m ChainwebTransaction buildCwCmd cmd = buildRawCmd cmd >>= \c -> case verifyCommand c of ProcSucc r -> return $ fmap (mkPayloadWithText c) r diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index c0c77ccb1c..bf4139b8e0 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -182,6 +182,7 @@ mkChainwebTxs' :: [Command Text] -> IO [ChainwebTransaction] mkChainwebTxs' rawTxs = forM rawTxs $ \cmd -> do let cmdBS = fmap TE.encodeUtf8 cmd + -- TODO: Use the new `assertCommand` function. procCmd = verifyCommand cmdBS case procCmd of f@ProcFail{} -> fail (show f) From 62d3270e291a48c45a9513c50de7c0d4ebfa04f9 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:06:30 -0500 Subject: [PATCH 75/91] Update pact --- cabal.project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 0298cc8d21..91e6465a2b 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 5d54ff741c8233b1ff9f7a38f763be17b7ba19ab - --sha256: sha256-VubTLojYnM7PJ1Y4Du+IkCypVQKlNHUH9RKfcpo6vUQ= + tag: 0e05fb9d4008a3d143e1842a88e9978ec8301276 + --sha256: 0zbjlrzmjzhbkw7kxrhqk360gx7a42qy2igffmsq88a3c8wzbg76 source-repository-package type: git From 95563b111aa0c317ae2940993180917eed7eb8bc Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:30:58 -0500 Subject: [PATCH 76/91] Revert KeysetPublicKey --- src/Chainweb/Miner/Config.hs | 6 +++--- src/Chainweb/Miner/Pact.hs | 4 ++-- src/Chainweb/Pact/Backend/ChainwebPactDb.hs | 2 +- .../Pact/Backend/RelationalCheckpointer.hs | 2 +- src/Chainweb/Pact/TransactionExec.hs | 6 +++--- src/Chainweb/Pact/Utils.hs | 9 ++++----- src/Chainweb/Pact/Validations.hs | 2 +- src/Chainweb/Rosetta/Utils.hs | 17 ++++++----------- src/Chainweb/Version.hs | 6 +++--- src/Chainweb/Version/Development.hs | 2 +- src/Chainweb/Version/Guards.hs | 6 +++--- src/Chainweb/Version/Mainnet.hs | 2 +- src/Chainweb/Version/Testnet.hs | 2 +- test/Chainweb/Test/Pact/Checkpointer.hs | 2 +- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 10 +++++----- test/Chainweb/Test/Pact/RemotePactTest.hs | 2 +- test/Chainweb/Test/Pact/SPV.hs | 2 +- test/Chainweb/Test/Pact/TransactionTests.hs | 4 ++-- test/Chainweb/Test/Pact/Utils.hs | 2 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 8 ++++---- test/Chainweb/Test/TestVersions.hs | 5 ++--- 21 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/Chainweb/Miner/Config.hs b/src/Chainweb/Miner/Config.hs index ff068c85f2..eb5ce112bb 100644 --- a/src/Chainweb/Miner/Config.hs +++ b/src/Chainweb/Miner/Config.hs @@ -50,7 +50,7 @@ import Numeric.Natural (Natural) import Options.Applicative import qualified Pact.JSON.Encode as J -import Pact.Types.Term (mkKeySetText, PublicKeyText(..)) +import Pact.Types.Term (mkKeySet, PublicKeyText(..)) -- internal modules @@ -204,7 +204,7 @@ pMiner prefix = pkToMiner <$> pPk where pkToMiner pk = Miner (MinerId $ "k:" <> _pubKey pk) - (MinerKeys $ mkKeySetText [pk] "keys-all") + (MinerKeys $ mkKeySet [pk] "keys-all") pPk = strOption % long (prefix <> "mining-public-key") <> help "public key of a miner in hex decimal encoding. The account name is the public key prefix by 'k:'. (This option can be provided multiple times.)" @@ -258,4 +258,4 @@ defaultNodeMining = NodeMiningConfig } invalidMiner :: Miner -invalidMiner = Miner "" . MinerKeys $ mkKeySetText [] "keys-all" +invalidMiner = Miner "" . MinerKeys $ mkKeySet [] "keys-all" diff --git a/src/Chainweb/Miner/Pact.hs b/src/Chainweb/Miner/Pact.hs index 2efb8887be..fbfc55ddd9 100644 --- a/src/Chainweb/Miner/Pact.hs +++ b/src/Chainweb/Miner/Pact.hs @@ -62,7 +62,7 @@ import Chainweb.Payload import Chainweb.Utils import qualified Pact.JSON.Encode as J -import Pact.Types.Term (KeySet(..), mkKeySet, mkKeySetText) +import Pact.Types.Term (KeySet(..), mkKeySet) -- -------------------------------------------------------------------------- -- -- Miner data @@ -125,7 +125,7 @@ minerKeys = lens (\(Miner _ k) -> k) (\(Miner i _) b -> Miner i b) defaultMiner :: Miner defaultMiner = Miner (MinerId "miner") $ MinerKeys - $ mkKeySetText + $ mkKeySet ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "keys-all" diff --git a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs index 96f3e8680c..31964c2850 100644 --- a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs +++ b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs @@ -346,7 +346,7 @@ doKeys d = do collect pb `DL.append` maybe DL.empty collect mptx let !allKeys = fmap fromString - $ msort -- becomes available with pact420Upgrade + $ msort -- becomes available with Pact42Upgrade $ LHM.sort $ dbKeys ++ memKeys return allKeys diff --git a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs index 8022013eda..e47adff94e 100644 --- a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs +++ b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs @@ -159,7 +159,7 @@ doRestore v cid dbenv (Just (bh, hash)) = runBlockEnv dbenv $ do where -- Module name fix follows the restore call to checkpointer. setModuleNameFix = bsModuleNameFix .= enableModuleNameFix v cid bh - setSortedKeys = bsSortedKeys .= pact420 v cid bh + setSortedKeys = bsSortedKeys .= pact42 v cid bh setLowerCaseTables = bsLowerCaseTables .= chainweb217Pact v cid bh doRestore _ _ dbenv Nothing = runBlockEnv dbenv $ do clearPendingTxState diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index 059c973713..b0e7c3a6b8 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -343,7 +343,7 @@ flagsFor :: ChainwebVersion -> V.ChainId -> BlockHeight -> S.Set ExecutionFlag flagsFor v cid bh = S.fromList $ concat [ enablePactEvents' v cid bh , enablePact40 v cid bh - , enablePact420 v cid bh + , enablePact42 v cid bh -- , enforceKeysetFormats' v cid bh , enablePactModuleMemcheck v cid bh , enablePact43 v cid bh @@ -751,8 +751,8 @@ enforceKeysetFormats' v cid bh = [FlagEnforceKeyFormats | enforceKeysetFormats v enablePact40 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePact40 v cid bh = [FlagDisablePact40 | not (pact4Coin3 v cid bh)] -enablePact420 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] -enablePact420 v cid bh = [FlagDisablePact420 | not (pact420 v cid bh)] +enablePact42 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact42 v cid bh = [FlagDisablePact42 | not (pact42 v cid bh)] enablePactModuleMemcheck :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePactModuleMemcheck v cid bh = [FlagDisableInlineMemCheck | not (chainweb213Pact v cid bh)] diff --git a/src/Chainweb/Pact/Utils.hs b/src/Chainweb/Pact/Utils.hs index 3e2869d3df..e0a36d3408 100644 --- a/src/Chainweb/Pact/Utils.hs +++ b/src/Chainweb/Pact/Utils.hs @@ -37,8 +37,7 @@ import Pact.Parse import qualified Pact.Types.ChainId as P import qualified Pact.Types.Term as P import Pact.Types.ChainMeta -import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), ed25519HexFormat) -import Pact.Types.Crypto (PPKScheme(ED25519)) +import Pact.Types.KeySet (ed25519HexFormat) import qualified Pact.JSON.Encode as J @@ -73,7 +72,7 @@ validateKAccount acctName = case T.take 2 acctName of "k:" -> let pubKey = P.PublicKeyText $ T.drop 2 acctName - in ed25519HexFormat $ KeysetPublicKey pubKey ED25519 + in ed25519HexFormat pubKey _ -> False extractPubKeyFromKAccount :: T.Text -> Maybe P.PublicKeyText @@ -90,14 +89,14 @@ generateKAccountFromPubKey pubKey | otherwise = Nothing where validPubKey :: Bool - validPubKey = ed25519HexFormat $ KeysetPublicKey pubKey ED25519 + validPubKey = ed25519HexFormat pubKey -- Warning: Only use if already certain that PublicKeyText -- is valid. -- Note: We are assuming the k: account is ED25519. pubKeyToKAccountKeySet :: P.PublicKeyText -> P.KeySet -pubKeyToKAccountKeySet pubKey = P.mkKeySetText [pubKey] "keys-all" +pubKeyToKAccountKeySet pubKey = P.mkKeySet [pubKey] "keys-all" generateKeySetFromKAccount :: T.Text -> Maybe P.KeySet generateKeySetFromKAccount kacct = do diff --git a/src/Chainweb/Pact/Validations.hs b/src/Chainweb/Pact/Validations.hs index 08c3b9a145..09eea2ec14 100644 --- a/src/Chainweb/Pact/Validations.hs +++ b/src/Chainweb/Pact/Validations.hs @@ -164,7 +164,7 @@ assertValidateSigs validSchemes hsh signers sigs | otherwise = and $ zipWith verifyUserSig sigs signers where verifyUserSig sig signer = let sigScheme = fromMaybe P.ED25519 (P._siScheme signer) - in sigScheme `elem` validSchemes && P.verifyUserSig hsh sig signer + in sigScheme `elem` validSchemes && isRight (P.verifyUserSig hsh sig signer) -- prop_tx_ttl_newBlock/validateBlock -- diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index 111e059326..b3101af880 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -22,7 +22,6 @@ import Data.Foldable (foldl') import Data.Decimal ( Decimal, DecimalRaw(Decimal) ) import Data.Hashable (Hashable(..)) import Data.List (sortOn, inits) -import Data.Maybe (mapMaybe) import Data.Word (Word32, Word64) import Text.Read (readMaybe) import Text.Printf ( printf ) @@ -37,6 +36,7 @@ import qualified Data.Text.Encoding as T import qualified Pact.Types.Runtime as P import qualified Pact.Types.RPC as P import qualified Pact.Types.Command as P +import qualified Pact.Types.Crypto as P import qualified Pact.Parse as P import qualified Data.Set as S import Data.Maybe ( fromMaybe ) @@ -46,7 +46,6 @@ import Numeric.Natural ( Natural ) import Pact.Types.Command import Pact.Types.PactValue (PactValue(..)) -import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), PublicKeyText(..)) import Pact.Types.Exp (Literal(..)) import Pact.JSON.Legacy.Value @@ -721,7 +720,7 @@ createSigningPayloads (EnrichedCommand cmd _ _) = map f toRosettaSigType Nothing = Just RosettaEd25519 toRosettaSigType (Just P.ED25519) = Just RosettaEd25519 - toRosettaSigType (Just P.WebAuthn) = Nothing + toRosettaSigType (Just P.WebAuthn) = Nothing -- TODO: Linda Ortega (09/18/2023) -- Returning `Nothing` to discourage using WebAuthn for Rosetta. `sigToScheme` will eventually throw an error. -------------------------------------------------------------------------------- @@ -786,16 +785,16 @@ matchSigs sigs signers = do $ HM.lookup addr m sigAndAddr (RosettaSignature _ (RosettaPublicKey pk ct) sigTyp sig) = do - _ <- toRosettaError RosettaInvalidSignature $! P.parseB16TextOnly sig + sigDecoded <- toRosettaError RosettaInvalidSignature $! P.parseB16TextOnly sig sigScheme <- sigToScheme sigTyp pkScheme <- getScheme ct when (sigScheme /= pkScheme) (Left $ stringRosettaError RosettaInvalidSignature $ "Expected the same Signature and PublicKey type for Signature=" ++ show sig) - let userSig = P.ED25519Sig sig + userSig <- toRosettaError RosettaInvalidSignature $! P.parseEd25519Signature sigDecoded addr <- toPactPubKeyAddr pk - pure (addr, userSig) + pure (addr, P.ED25519Sig userSig) -------------------------------------------------------------------------------- -- Rosetta Helper Types -- @@ -1182,11 +1181,7 @@ toRosettaError failure = annotate (stringRosettaError failure) ksToPubKeys :: P.KeySet -> [T.Text] ksToPubKeys (P.KeySet pkSet _) = - mapMaybe (\(KeysetPublicKey (PublicKeyText pk) sch) -> - if sch == ED25519 - then Just pk - else Nothing - ) (S.toList pkSet) + map P._pubKey (S.toList pkSet) parsePubKeys :: T.Text -> Value -> Either RosettaError [T.Text] diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 36c27c3300..2e0cadb84e 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -179,7 +179,7 @@ data Fork | SPVBridge | Pact4Coin3 | EnforceKeysetFormats - | Pact420 + | Pact42 | CheckTxHash | Chainweb213Pact | Chainweb214Pact @@ -210,7 +210,7 @@ instance HasTextRepresentation Fork where toText SPVBridge = "spvBridge" toText Pact4Coin3 = "pact4Coin3" toText EnforceKeysetFormats = "enforceKeysetFormats" - toText Pact420 = "pact420" + toText Pact42 = "Pact42" toText CheckTxHash = "checkTxHash" toText Chainweb213Pact = "chainweb213Pact" toText Chainweb214Pact = "chainweb214Pact" @@ -237,7 +237,7 @@ instance HasTextRepresentation Fork where fromText "spvBridge" = return SPVBridge fromText "pact4Coin3" = return Pact4Coin3 fromText "enforceKeysetFormats" = return EnforceKeysetFormats - fromText "pact420" = return Pact420 + fromText "Pact42" = return Pact42 fromText "checkTxHash" = return CheckTxHash fromText "chainweb213Pact" = return Chainweb213Pact fromText "chainweb214Pact" = return Chainweb214Pact diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 2d3e3b2eeb..172c94126b 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -55,7 +55,7 @@ devnet = ChainwebVersion SPVBridge -> AllChains $ ForkAtBlockHeight $ BlockHeight 50 Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 80 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight $ BlockHeight 100 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 90 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 90 CheckTxHash -> AllChains $ ForkAtBlockHeight $ BlockHeight 110 Chainweb213Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 95 Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 115 diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index ac1280dc49..6b09cd773a 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -30,7 +30,7 @@ module Chainweb.Version.Guards , enablePactEvents , enableSPVBridge , pact4Coin3 - , pact420 + , pact42 , enforceKeysetFormats , doCheckTxHash , chainweb213Pact @@ -208,8 +208,8 @@ pact44NewTrans = checkFork atOrAfter Pact44NewTrans pact4Coin3 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pact4Coin3 = checkFork after Pact4Coin3 -pact420 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pact420 = checkFork atOrAfter Pact420 +pact42 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pact42 = checkFork atOrAfter Pact42 chainweb213Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb213Pact = checkFork atOrAfter Chainweb213Pact diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 75494fb991..9cbda87313 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -129,7 +129,7 @@ mainnet = ChainwebVersion SPVBridge -> AllChains (ForkAtBlockHeight $ BlockHeight 1_275_000) Pact4Coin3 -> AllChains (ForkAtBlockHeight $ BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 EnforceKeysetFormats -> AllChains (ForkAtBlockHeight $ BlockHeight 2_162_000) -- 2022-01-17T17:51:12 - Pact420 -> AllChains (ForkAtBlockHeight $ BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 + Pact42 -> AllChains (ForkAtBlockHeight $ BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 CheckTxHash -> AllChains (ForkAtBlockHeight $ BlockHeight 2_349_800) -- 2022-01-23T02:53:38 Chainweb213Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 Chainweb214Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index c6e198c052..a0e4349ce0 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -109,7 +109,7 @@ testnet = ChainwebVersion SPVBridge -> AllChains $ ForkAtBlockHeight $ BlockHeight 820_000 -- 2021-01-14T17:12:02 Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_261_000 -- 2021-06-17T15:54:14 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_701_000 -- 2021-11-18T17:54:36 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_862_000 -- 2021-06-19T03:34:05 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_862_000 -- 2021-06-19T03:34:05 CheckTxHash -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_889_000 -- 2022-01-24T04:19:24 Chainweb213Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_974_556 -- 2022-02-25 00:00:00 Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_134_331 -- 2022-04-21T12:00:00Z diff --git a/test/Chainweb/Test/Pact/Checkpointer.hs b/test/Chainweb/Test/Pact/Checkpointer.hs index fa4ca9e189..2a4f1d4c11 100644 --- a/test/Chainweb/Test/Pact/Checkpointer.hs +++ b/test/Chainweb/Test/Pact/Checkpointer.hs @@ -505,7 +505,7 @@ runRegression pactdb e schemaInit = do let row' = RowData RDV1 $ ObjectMap $ M.fromList [("gah",toPV False),("fh",toPV (1 :: Int))] _writeRow pactdb Update usert "key1" row' conn assertEquals' "user update" (Just row') (_readRow pactdb usert "key1" conn) - let ks = mkKeySetText [PublicKeyText "skdjhfskj"] "predfun" + let ks = mkKeySet [PublicKeyText "skdjhfskj"] "predfun" _writeRow pactdb Write KeySets "ks1" ks conn assertEquals' "keyset write" (Just ks) $ _readRow pactdb KeySets "ks1" conn (modName,modRef,mod') <- loadModule diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index ba80fbe5c5..32431939b5 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -116,7 +116,7 @@ data PactTxTest = PactTxTest tests :: TestTree tests = testGroup testName [ test generousConfig freeGasModel "pact4coin3UpgradeTest" pact4coin3UpgradeTest - , test generousConfig freeGasModel "pact420UpgradeTest" pact420UpgradeTest + , test generousConfig freeGasModel "Pact42UpgradeTest" Pact42UpgradeTest , test generousConfig freeGasModel "minerKeysetTest" minerKeysetTest , test timeoutConfig freeGasModel "txTimeoutTest" txTimeoutTest , test generousConfig getGasModel "chainweb213Test" chainweb213Test @@ -165,7 +165,7 @@ minerKeysetTest = do where - badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySetText ["bad-bad-bad"] "keys-all" + badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySet ["bad-bad-bad"] "keys-all" txTimeoutTest :: PactTestM () txTimeoutTest = do @@ -648,8 +648,8 @@ pact431UpgradeTest = do ]) -pact420UpgradeTest :: PactTestM () -pact420UpgradeTest = do +Pact42UpgradeTest :: PactTestM () +Pact42UpgradeTest = do -- run past genesis, upgrades runToHeight 3 @@ -1114,7 +1114,7 @@ pact4coin3UpgradeTest = do , PactTxTest badKeyset $ assertTxSuccess "Should allow bad keys" $ - pKeySet $ mkKeySetText ["badkey"] "keys-all" + pKeySet $ mkKeySet ["badkey"] "keys-all" ] assertTxEvents "Coinbase events @ block 7" [] =<< cbResult diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 895041998d..192daf5539 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -881,7 +881,7 @@ allocationTest t cenv step = do tx3 = let c = "(define-keyset \"allocation02\" (read-keyset \"allocation02-keyset\"))" - d = mkKeySetText + d = mkKeySet ["0c8212a903f6442c84acd0069acc263c69434b5af37b2997b16d6348b53fcd0a"] "keys-all" in PactTransaction c $ Just (A.object [ "allocation02-keyset" A..= J.toJsonViaEncode d ]) diff --git a/test/Chainweb/Test/Pact/SPV.hs b/test/Chainweb/Test/Pact/SPV.hs index 2c49148e76..d99bfbef07 100644 --- a/test/Chainweb/Test/Pact/SPV.hs +++ b/test/Chainweb/Test/Pact/SPV.hs @@ -402,7 +402,7 @@ burnGen time pidv sid tid = do tx1Data = -- sender01 keyset guard - let ks = mkKeySetText + let ks = mkKeySet ["6be2f485a7af75fedb4b7f153a903f7e6000ca4aa501179c91a2450b777bd2a7"] "keys-all" diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 39b19e1b7e..63d8b9d770 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -196,7 +196,7 @@ badMinerId :: MinerId badMinerId = MinerId "alpha\" (read-keyset \"miner-keyset\") 9999999.99)(coin.coinbase \"alpha" minerKeys0 :: MinerKeys -minerKeys0 = MinerKeys $ mkKeySetText +minerKeys0 = MinerKeys $ mkKeySet ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "default" @@ -267,7 +267,7 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do miner = Miner (MinerId "tester01\" (read-keyset \"miner-keyset\") 1000.0)(coin.coinbase \"tester01") - (MinerKeys $ mkKeySetText ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") + (MinerKeys $ mkKeySet ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") preForkHeight = 121451 postForkHeight = 121452 diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index e7bd554f9e..abe29dd80f 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -237,7 +237,7 @@ mkKeySetData :: Key -> [SimpleKeyPair] -> Value mkKeySetData name keys = object [ name .= map fst keys ] sender00Ks :: KeySet -sender00Ks = mkKeySetText +sender00Ks = mkKeySet ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] "keys-all" diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index a57a793efc..3fc2b576bf 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -140,7 +140,7 @@ tests rdb = testGroup "Chainweb.Test.Rosetta.RestAPI" go , networkListTests , networkOptionsTests , networkStatusTests - , blockKAccountAfterPact420 + , blockKAccountAfterPact42 , constructionTransferTests , blockCoinV3RemediationTests ] @@ -177,8 +177,8 @@ accountBalanceTests tio envIo = -- TxLog parse error after fork to Pact 420. -- This assumes that this test occurs after the -- fork blockheight. -blockKAccountAfterPact420 :: RosettaTest -blockKAccountAfterPact420 tio envIo = +blockKAccountAfterPact42 :: RosettaTest +blockKAccountAfterPact42 tio envIo = testCaseSteps "Block k Account After Pact 420 Test" $ \step -> do cenv <- envIo rkmv <- newEmptyMVar @RequestKeys @@ -491,7 +491,7 @@ constructionTransferTests _ envIo = ks (TestKeySet _ Nothing pred') = P.mkKeySet [] pred' ks (TestKeySet _ (Just (pk,_)) pred') = - P.mkKeySetText [P.PublicKeyText pk] pred' + P.mkKeySet [P.PublicKeyText pk] pred' sender00KAcct = "k:" <> fst sender00 sender01KAcct = "k:" <> fst sender01 diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index a79066ae13..29cffaefcc 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -126,7 +126,7 @@ fastForks = tabulateHashMap $ \case Chainweb213Pact -> AllChains ForkAtGenesis PactEvents -> AllChains ForkAtGenesis CoinV2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 ModuleNameFix -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 ModuleNameFix2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 @@ -246,7 +246,7 @@ slowForkingCpmTestVersion g = buildTestVersion $ \v -> v SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight (BlockHeight 2) ModuleNameFix -> AllChains $ ForkAtBlockHeight (BlockHeight 2) ModuleNameFix2 -> AllChains $ ForkAtBlockHeight (BlockHeight 2) - Pact420 -> AllChains $ ForkAtBlockHeight (BlockHeight 5) + Pact42 -> AllChains $ ForkAtBlockHeight (BlockHeight 5) CheckTxHash -> AllChains $ ForkAtBlockHeight (BlockHeight 7) EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight (BlockHeight 10) PactEvents -> AllChains $ ForkAtBlockHeight (BlockHeight 10) @@ -275,4 +275,3 @@ noBridgeCpmTestVersion g = buildTestVersion $ \v -> v & cpmTestVersion g & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) & versionForks .~ (fastForks & at SPVBridge ?~ AllChains ForkNever) - From 62c9a99f454a2fcce50bb27b4cf24428eb8329bb Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Wed, 8 Nov 2023 14:57:07 -0800 Subject: [PATCH 77/91] Work the validateCommand function with chainid and version through more of chainweb. Next stop: execBlock --- bench/Chainweb/Pact/Backend/ForkingBench.hs | 2 + src/Chainweb/Pact/RestAPI/Server.hs | 43 ++++++++++++++------- src/Chainweb/Rosetta/RestAPI/Server.hs | 15 +++---- test/Chainweb/Test/Pact/Utils.hs | 1 + tools/ea/Ea.hs | 1 + 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/bench/Chainweb/Pact/Backend/ForkingBench.hs b/bench/Chainweb/Pact/Backend/ForkingBench.hs index fc5d8b819b..b86e93653a 100644 --- a/bench/Chainweb/Pact/Backend/ForkingBench.hs +++ b/bench/Chainweb/Pact/Backend/ForkingBench.hs @@ -450,6 +450,8 @@ formatB16PubKey = toB16Text . getPublic safeCapitalize :: String -> String safeCapitalize = maybe [] (uncurry (:) . bimap toUpper (Prelude.map toLower)) . Data.List.uncons + +-- TODO: Use the new `assertCommand` function. validateCommand :: Command Text -> Either String ChainwebTransaction validateCommand cmdText = case verifyCommand cmdBS of ProcSucc cmd -> Right (mkPayloadWithTextOld <$> cmd) diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index 39e27b4821..5aedfbfb49 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -46,7 +46,6 @@ import Control.Monad.Trans.Maybe import Data.Aeson as Aeson import Data.Bifunctor (second) -import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Char8 as BSL8 import qualified Data.ByteString.Short as SB @@ -118,6 +117,8 @@ import Chainweb.Transaction import qualified Chainweb.TreeDB as TreeDB import Chainweb.Utils import Chainweb.Version +import Chainweb.Pact.Validations (assertCommand) +import Chainweb.Version.Guards (validPPKSchemes) import Chainweb.WebPactExecutionService import Chainweb.Storage.Table @@ -126,6 +127,7 @@ import qualified Pact.JSON.Encode as J import qualified Pact.Parse as Pact import Pact.Types.API import qualified Pact.Types.ChainId as Pact +import Pact.Types.ChainMeta (PublicMeta) import Pact.Types.Command import Pact.Types.Hash (Hash(..)) import qualified Pact.Types.Hash as Pact @@ -186,12 +188,13 @@ pactServer d = logger = _pactServerDataLogger d pact = _pactServerDataPact d cdb = _pactServerDataCutDb d + v = _chainwebVersion cdb pactApiHandlers - = sendHandler logger mempool + = sendHandler logger v cid mempool :<|> pollHandler logger cdb cid pact mempool :<|> listenHandler logger cdb cid pact mempool - :<|> localHandler logger pact + :<|> localHandler logger v cid pact pactSpvHandler = spvHandler logger cdb cid pactSpv2Handler = spv2Handler logger cdb cid @@ -247,12 +250,14 @@ instance ToJSON PactCmdLog where sendHandler :: Logger logger => logger + -> ChainwebVersion + -> ChainId -> MempoolBackend ChainwebTransaction -> SubmitBatch -> Handler RequestKeys -sendHandler logger mempool (SubmitBatch cmds) = Handler $ do +sendHandler logger v cid mempool (SubmitBatch cmds) = Handler $ do liftIO $ logg Info (PactCmdLogSend cmds) - case traverse validateCommand cmds of + case traverse (validateCommand v cid) cmds of Right enriched -> do let txs = V.fromList $ NEL.toList enriched -- If any of the txs in the batch fail validation, we reject them all. @@ -365,6 +370,8 @@ listenHandler logger cdb cid pact mem (ListenerRequest key) = do localHandler :: Logger logger => logger + -> ChainwebVersion + -> ChainId -> PactExecutionService -> Maybe LocalPreflightSimulation -- ^ Preflight flag @@ -374,7 +381,7 @@ localHandler -- ^ Rewind depth -> Command Text -> Handler LocalResult -localHandler logger pact preflight sigVerify rewindDepth cmd = do +localHandler logger v cid pact preflight sigVerify rewindDepth cmd = do liftIO $ logg Info $ PactCmdLogLocal cmd cmd' <- case doCommandValidation cmd of Right c -> return c @@ -409,7 +416,7 @@ localHandler logger pact preflight sigVerify rewindDepth cmd = do let cmd' = cmdBS { _cmdPayload = p } pure $ mkPayloadWithText cmdBS <$> cmd' - | otherwise = validateCommand cmd + | otherwise = validateCommand v cid cmd -- -------------------------------------------------------------------------- -- -- Cross Chain SPV Handler @@ -686,13 +693,23 @@ internalPoll pdb bhdb mempool pactEx cut confDepth requestKeys0 = do toPactTx :: Transaction -> Maybe (Command Text) toPactTx (Transaction b) = decodeStrict' b -validateCommand :: Command Text -> Either String ChainwebTransaction -validateCommand cmdText = case verifyCommand cmdBS of - ProcSucc cmd -> Right (mkPayloadWithText cmdBS <$> cmd) - ProcFail err -> Left err + +validateCommand :: ChainwebVersion -> ChainId -> Command Text -> Either String ChainwebTransaction +validateCommand v cid cmdText = case parsedPayload of + Right (parsedPact :: Payload PublicMeta ParsedCode) -> + let pwt = mkPayloadWithText cmdBs parsedPact + commandParsed = cmdText { _cmdPayload = pwt } + in + if assertCommand commandParsed (validPPKSchemes v cid maxBound) + then Right commandParsed + else Left "Command failed validation" + Left e -> Left $ "Pact parsing error: " ++ e + where - cmdBS :: Command ByteString - cmdBS = encodeUtf8 <$> cmdText + payloadBs = encodeUtf8 (_cmdPayload cmdText) + cmdBs = cmdText { _cmdPayload = payloadBs } + parsedPayload = traverse (parsePact (maxBound :: PactParserVersion)) + =<< Aeson.eitherDecodeStrict' payloadBs -- | Validate the length of the request key's underlying hash. diff --git a/src/Chainweb/Rosetta/RestAPI/Server.hs b/src/Chainweb/Rosetta/RestAPI/Server.hs index 52112d7e1b..15ea1e6d33 100644 --- a/src/Chainweb/Rosetta/RestAPI/Server.hs +++ b/src/Chainweb/Rosetta/RestAPI/Server.hs @@ -345,17 +345,18 @@ constructionParseH :: ChainwebVersion -> ConstructionParseReq -> Handler ConstructionParseResp -constructionParseH v (ConstructionParseReq net isSigned tx) = +constructionParseH v (ConstructionParseReq net isSigned tx) = do either throwRosettaError pure work where work :: Either RosettaError ConstructionParseResp work = do + cid <- annotate rosettaError' (validateNetwork v net) void $ annotate rosettaError' (validateNetwork v net) (EnrichedCommand cmd txInfo signAccts) <- note (rosettaError' RosettaUnparsableTx) $ textToEnrichedCommand tx - signers <- getRosettaSigners cmd signAccts + signers <- getRosettaSigners cid cmd signAccts let ops = txToOps txInfo pure $ ConstructionParseResp @@ -365,9 +366,9 @@ constructionParseH v (ConstructionParseReq net isSigned tx) = , _constructionParseResp_metadata = Nothing } - getRosettaSigners cmd expectedSignerAccts + getRosettaSigners cid cmd expectedSignerAccts | isSigned = do - _ <- toRosettaError RosettaInvalidTx $ validateCommand cmd + _ <- toRosettaError RosettaInvalidTx $ validateCommand v cid cmd pure expectedSignerAccts -- If transaction signatures successfully validates, -- it was signed correctly with all of the account public @@ -379,7 +380,7 @@ constructionParseH v (ConstructionParseReq net isSigned tx) = constructionCombineH :: ConstructionCombineReq -> Handler ConstructionCombineResp -constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = +constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = do either throwRosettaError pure work where work :: Either RosettaError ConstructionCombineResp @@ -398,7 +399,7 @@ constructionCombineH (ConstructionCombineReq _ unsignedTx sigs) = constructionHashH :: ConstructionHashReq -> Handler TransactionIdResp -constructionHashH (ConstructionHashReq _ signedTx) = +constructionHashH (ConstructionHashReq _ signedTx) = do either throwRosetta pure work where work :: Either RosettaFailure TransactionIdResp @@ -437,7 +438,7 @@ constructionSubmitH v ms (ConstructionSubmitReq net tx) = note (rosettaError' RosettaUnparsableTx) $ textToEnrichedCommand tx - case validateCommand cmd of + case validateCommand v cid cmd of Right validated -> do let txs = V.fromList [validated] -- If any of the txs in the batch fail validation, we reject them all. diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index abe29dd80f..3de29f6a46 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -522,6 +522,7 @@ mkCmd nonce rpc = defaultCmd -- | Build parsed + verified Pact command -- +-- TODO: Use the new `assertCommand` function. buildCwCmd :: (MonadThrow m, MonadIO m) => CmdBuilder -> m ChainwebTransaction buildCwCmd cmd = buildRawCmd cmd >>= \c -> case verifyCommand c of ProcSucc r -> return $ fmap (mkPayloadWithText c) r diff --git a/tools/ea/Ea.hs b/tools/ea/Ea.hs index c0c77ccb1c..bf4139b8e0 100644 --- a/tools/ea/Ea.hs +++ b/tools/ea/Ea.hs @@ -182,6 +182,7 @@ mkChainwebTxs' :: [Command Text] -> IO [ChainwebTransaction] mkChainwebTxs' rawTxs = forM rawTxs $ \cmd -> do let cmdBS = fmap TE.encodeUtf8 cmd + -- TODO: Use the new `assertCommand` function. procCmd = verifyCommand cmdBS case procCmd of f@ProcFail{} -> fail (show f) From 7433e75815bab5fd5b476ae4ed6e138f74b6473d Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:53:12 -0500 Subject: [PATCH 78/91] fix tests --- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 6 +++--- test/Chainweb/Test/Rosetta/RestAPI.hs | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 32431939b5..95f6657141 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -116,7 +116,7 @@ data PactTxTest = PactTxTest tests :: TestTree tests = testGroup testName [ test generousConfig freeGasModel "pact4coin3UpgradeTest" pact4coin3UpgradeTest - , test generousConfig freeGasModel "Pact42UpgradeTest" Pact42UpgradeTest + , test generousConfig freeGasModel "pact42UpgradeTest" pact42UpgradeTest , test generousConfig freeGasModel "minerKeysetTest" minerKeysetTest , test timeoutConfig freeGasModel "txTimeoutTest" txTimeoutTest , test generousConfig getGasModel "chainweb213Test" chainweb213Test @@ -648,8 +648,8 @@ pact431UpgradeTest = do ]) -Pact42UpgradeTest :: PactTestM () -Pact42UpgradeTest = do +pact42UpgradeTest :: PactTestM () +pact42UpgradeTest = do -- run past genesis, upgrades runToHeight 3 diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 3fc2b576bf..93653b19b6 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -600,14 +600,17 @@ submitToConstructionAPI expectOps chainId' payer getKeys expectResult cenv step (Right (hsh :: P.PactHash)) <- pure $ fmap (P.fromUntypedHash . P.Hash . BS.toShort) (P.parseB16TextOnly $ _rosettaSigningPayload_hexBytes payload) - ED25519Sig sig <- P.signHash hsh kp + let + sig = case P.signHash hsh kp of + ED25519Sig s -> s + _ -> error "not an ED25519 signature" pure $! RosettaSignature { _rosettaSignature_signingPayload = payload , _rosettaSignature_publicKey = RosettaPublicKey pk CurveEdwards25519 , _rosettaSignature_signatureType = RosettaEd25519 - , _rosettaSignature_hexBytes = sig + , _rosettaSignature_hexBytes = P.toB16Text $ P.exportEd25519Signature sig } acct n = AccountId n Nothing Nothing From a0428f66a0d702dcd06bc6e9f68b269295568e9c Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:06:30 -0500 Subject: [PATCH 79/91] Update pact --- cabal.project | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal.project b/cabal.project index 9bf3f1d11f..cfbba11863 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: bdbc0043c27c7ca0ea58bf6b034216f163079b57 - --sha256: sha256-U430nKNe3sEowX8yMjF8yLK0IoURakKsVecNnJQ/xAw= + tag: 15d391406e84b65945021b4066ce0cf0464bcc63 + --sha256: BROKEN source-repository-package type: git From f4b6260c0a0f372ae5746e84da30fae5cf6c9157 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:30:58 -0500 Subject: [PATCH 80/91] Revert KeysetPublicKey --- src/Chainweb/Miner/Config.hs | 6 +++--- src/Chainweb/Miner/Pact.hs | 4 ++-- src/Chainweb/Pact/Backend/ChainwebPactDb.hs | 2 +- .../Pact/Backend/RelationalCheckpointer.hs | 2 +- src/Chainweb/Pact/TransactionExec.hs | 6 +++--- src/Chainweb/Pact/Utils.hs | 9 ++++----- src/Chainweb/Pact/Validations.hs | 2 +- src/Chainweb/Rosetta/Utils.hs | 17 ++++++----------- src/Chainweb/Version.hs | 6 +++--- src/Chainweb/Version/Development.hs | 2 +- src/Chainweb/Version/Guards.hs | 6 +++--- src/Chainweb/Version/Mainnet.hs | 2 +- src/Chainweb/Version/Testnet.hs | 2 +- test/Chainweb/Test/Pact/Checkpointer.hs | 2 +- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 10 +++++----- test/Chainweb/Test/Pact/RemotePactTest.hs | 2 +- test/Chainweb/Test/Pact/SPV.hs | 2 +- test/Chainweb/Test/Pact/TransactionTests.hs | 4 ++-- test/Chainweb/Test/Pact/Utils.hs | 2 +- test/Chainweb/Test/Rosetta/RestAPI.hs | 8 ++++---- test/Chainweb/Test/TestVersions.hs | 5 ++--- 21 files changed, 47 insertions(+), 54 deletions(-) diff --git a/src/Chainweb/Miner/Config.hs b/src/Chainweb/Miner/Config.hs index ff068c85f2..eb5ce112bb 100644 --- a/src/Chainweb/Miner/Config.hs +++ b/src/Chainweb/Miner/Config.hs @@ -50,7 +50,7 @@ import Numeric.Natural (Natural) import Options.Applicative import qualified Pact.JSON.Encode as J -import Pact.Types.Term (mkKeySetText, PublicKeyText(..)) +import Pact.Types.Term (mkKeySet, PublicKeyText(..)) -- internal modules @@ -204,7 +204,7 @@ pMiner prefix = pkToMiner <$> pPk where pkToMiner pk = Miner (MinerId $ "k:" <> _pubKey pk) - (MinerKeys $ mkKeySetText [pk] "keys-all") + (MinerKeys $ mkKeySet [pk] "keys-all") pPk = strOption % long (prefix <> "mining-public-key") <> help "public key of a miner in hex decimal encoding. The account name is the public key prefix by 'k:'. (This option can be provided multiple times.)" @@ -258,4 +258,4 @@ defaultNodeMining = NodeMiningConfig } invalidMiner :: Miner -invalidMiner = Miner "" . MinerKeys $ mkKeySetText [] "keys-all" +invalidMiner = Miner "" . MinerKeys $ mkKeySet [] "keys-all" diff --git a/src/Chainweb/Miner/Pact.hs b/src/Chainweb/Miner/Pact.hs index 2efb8887be..fbfc55ddd9 100644 --- a/src/Chainweb/Miner/Pact.hs +++ b/src/Chainweb/Miner/Pact.hs @@ -62,7 +62,7 @@ import Chainweb.Payload import Chainweb.Utils import qualified Pact.JSON.Encode as J -import Pact.Types.Term (KeySet(..), mkKeySet, mkKeySetText) +import Pact.Types.Term (KeySet(..), mkKeySet) -- -------------------------------------------------------------------------- -- -- Miner data @@ -125,7 +125,7 @@ minerKeys = lens (\(Miner _ k) -> k) (\(Miner i _) b -> Miner i b) defaultMiner :: Miner defaultMiner = Miner (MinerId "miner") $ MinerKeys - $ mkKeySetText + $ mkKeySet ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "keys-all" diff --git a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs index 96f3e8680c..31964c2850 100644 --- a/src/Chainweb/Pact/Backend/ChainwebPactDb.hs +++ b/src/Chainweb/Pact/Backend/ChainwebPactDb.hs @@ -346,7 +346,7 @@ doKeys d = do collect pb `DL.append` maybe DL.empty collect mptx let !allKeys = fmap fromString - $ msort -- becomes available with pact420Upgrade + $ msort -- becomes available with Pact42Upgrade $ LHM.sort $ dbKeys ++ memKeys return allKeys diff --git a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs index 8022013eda..e47adff94e 100644 --- a/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs +++ b/src/Chainweb/Pact/Backend/RelationalCheckpointer.hs @@ -159,7 +159,7 @@ doRestore v cid dbenv (Just (bh, hash)) = runBlockEnv dbenv $ do where -- Module name fix follows the restore call to checkpointer. setModuleNameFix = bsModuleNameFix .= enableModuleNameFix v cid bh - setSortedKeys = bsSortedKeys .= pact420 v cid bh + setSortedKeys = bsSortedKeys .= pact42 v cid bh setLowerCaseTables = bsLowerCaseTables .= chainweb217Pact v cid bh doRestore _ _ dbenv Nothing = runBlockEnv dbenv $ do clearPendingTxState diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index cbc1dbec0d..5efeb6497c 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -343,7 +343,7 @@ flagsFor :: ChainwebVersion -> V.ChainId -> BlockHeight -> S.Set ExecutionFlag flagsFor v cid bh = S.fromList $ concat [ enablePactEvents' v cid bh , enablePact40 v cid bh - , enablePact420 v cid bh + , enablePact42 v cid bh , enforceKeysetFormats' v cid bh , enablePactModuleMemcheck v cid bh , enablePact43 v cid bh @@ -751,8 +751,8 @@ enforceKeysetFormats' v cid bh = [FlagEnforceKeyFormats | enforceKeysetFormats v enablePact40 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePact40 v cid bh = [FlagDisablePact40 | not (pact4Coin3 v cid bh)] -enablePact420 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] -enablePact420 v cid bh = [FlagDisablePact420 | not (pact420 v cid bh)] +enablePact42 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact42 v cid bh = [FlagDisablePact42 | not (pact42 v cid bh)] enablePactModuleMemcheck :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePactModuleMemcheck v cid bh = [FlagDisableInlineMemCheck | not (chainweb213Pact v cid bh)] diff --git a/src/Chainweb/Pact/Utils.hs b/src/Chainweb/Pact/Utils.hs index 3e2869d3df..e0a36d3408 100644 --- a/src/Chainweb/Pact/Utils.hs +++ b/src/Chainweb/Pact/Utils.hs @@ -37,8 +37,7 @@ import Pact.Parse import qualified Pact.Types.ChainId as P import qualified Pact.Types.Term as P import Pact.Types.ChainMeta -import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), ed25519HexFormat) -import Pact.Types.Crypto (PPKScheme(ED25519)) +import Pact.Types.KeySet (ed25519HexFormat) import qualified Pact.JSON.Encode as J @@ -73,7 +72,7 @@ validateKAccount acctName = case T.take 2 acctName of "k:" -> let pubKey = P.PublicKeyText $ T.drop 2 acctName - in ed25519HexFormat $ KeysetPublicKey pubKey ED25519 + in ed25519HexFormat pubKey _ -> False extractPubKeyFromKAccount :: T.Text -> Maybe P.PublicKeyText @@ -90,14 +89,14 @@ generateKAccountFromPubKey pubKey | otherwise = Nothing where validPubKey :: Bool - validPubKey = ed25519HexFormat $ KeysetPublicKey pubKey ED25519 + validPubKey = ed25519HexFormat pubKey -- Warning: Only use if already certain that PublicKeyText -- is valid. -- Note: We are assuming the k: account is ED25519. pubKeyToKAccountKeySet :: P.PublicKeyText -> P.KeySet -pubKeyToKAccountKeySet pubKey = P.mkKeySetText [pubKey] "keys-all" +pubKeyToKAccountKeySet pubKey = P.mkKeySet [pubKey] "keys-all" generateKeySetFromKAccount :: T.Text -> Maybe P.KeySet generateKeySetFromKAccount kacct = do diff --git a/src/Chainweb/Pact/Validations.hs b/src/Chainweb/Pact/Validations.hs index 08c3b9a145..09eea2ec14 100644 --- a/src/Chainweb/Pact/Validations.hs +++ b/src/Chainweb/Pact/Validations.hs @@ -164,7 +164,7 @@ assertValidateSigs validSchemes hsh signers sigs | otherwise = and $ zipWith verifyUserSig sigs signers where verifyUserSig sig signer = let sigScheme = fromMaybe P.ED25519 (P._siScheme signer) - in sigScheme `elem` validSchemes && P.verifyUserSig hsh sig signer + in sigScheme `elem` validSchemes && isRight (P.verifyUserSig hsh sig signer) -- prop_tx_ttl_newBlock/validateBlock -- diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index 111e059326..b3101af880 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -22,7 +22,6 @@ import Data.Foldable (foldl') import Data.Decimal ( Decimal, DecimalRaw(Decimal) ) import Data.Hashable (Hashable(..)) import Data.List (sortOn, inits) -import Data.Maybe (mapMaybe) import Data.Word (Word32, Word64) import Text.Read (readMaybe) import Text.Printf ( printf ) @@ -37,6 +36,7 @@ import qualified Data.Text.Encoding as T import qualified Pact.Types.Runtime as P import qualified Pact.Types.RPC as P import qualified Pact.Types.Command as P +import qualified Pact.Types.Crypto as P import qualified Pact.Parse as P import qualified Data.Set as S import Data.Maybe ( fromMaybe ) @@ -46,7 +46,6 @@ import Numeric.Natural ( Natural ) import Pact.Types.Command import Pact.Types.PactValue (PactValue(..)) -import Pact.Types.KeySet (KeysetPublicKey(KeysetPublicKey), PublicKeyText(..)) import Pact.Types.Exp (Literal(..)) import Pact.JSON.Legacy.Value @@ -721,7 +720,7 @@ createSigningPayloads (EnrichedCommand cmd _ _) = map f toRosettaSigType Nothing = Just RosettaEd25519 toRosettaSigType (Just P.ED25519) = Just RosettaEd25519 - toRosettaSigType (Just P.WebAuthn) = Nothing + toRosettaSigType (Just P.WebAuthn) = Nothing -- TODO: Linda Ortega (09/18/2023) -- Returning `Nothing` to discourage using WebAuthn for Rosetta. `sigToScheme` will eventually throw an error. -------------------------------------------------------------------------------- @@ -786,16 +785,16 @@ matchSigs sigs signers = do $ HM.lookup addr m sigAndAddr (RosettaSignature _ (RosettaPublicKey pk ct) sigTyp sig) = do - _ <- toRosettaError RosettaInvalidSignature $! P.parseB16TextOnly sig + sigDecoded <- toRosettaError RosettaInvalidSignature $! P.parseB16TextOnly sig sigScheme <- sigToScheme sigTyp pkScheme <- getScheme ct when (sigScheme /= pkScheme) (Left $ stringRosettaError RosettaInvalidSignature $ "Expected the same Signature and PublicKey type for Signature=" ++ show sig) - let userSig = P.ED25519Sig sig + userSig <- toRosettaError RosettaInvalidSignature $! P.parseEd25519Signature sigDecoded addr <- toPactPubKeyAddr pk - pure (addr, userSig) + pure (addr, P.ED25519Sig userSig) -------------------------------------------------------------------------------- -- Rosetta Helper Types -- @@ -1182,11 +1181,7 @@ toRosettaError failure = annotate (stringRosettaError failure) ksToPubKeys :: P.KeySet -> [T.Text] ksToPubKeys (P.KeySet pkSet _) = - mapMaybe (\(KeysetPublicKey (PublicKeyText pk) sch) -> - if sch == ED25519 - then Just pk - else Nothing - ) (S.toList pkSet) + map P._pubKey (S.toList pkSet) parsePubKeys :: T.Text -> Value -> Either RosettaError [T.Text] diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 36c27c3300..2e0cadb84e 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -179,7 +179,7 @@ data Fork | SPVBridge | Pact4Coin3 | EnforceKeysetFormats - | Pact420 + | Pact42 | CheckTxHash | Chainweb213Pact | Chainweb214Pact @@ -210,7 +210,7 @@ instance HasTextRepresentation Fork where toText SPVBridge = "spvBridge" toText Pact4Coin3 = "pact4Coin3" toText EnforceKeysetFormats = "enforceKeysetFormats" - toText Pact420 = "pact420" + toText Pact42 = "Pact42" toText CheckTxHash = "checkTxHash" toText Chainweb213Pact = "chainweb213Pact" toText Chainweb214Pact = "chainweb214Pact" @@ -237,7 +237,7 @@ instance HasTextRepresentation Fork where fromText "spvBridge" = return SPVBridge fromText "pact4Coin3" = return Pact4Coin3 fromText "enforceKeysetFormats" = return EnforceKeysetFormats - fromText "pact420" = return Pact420 + fromText "Pact42" = return Pact42 fromText "checkTxHash" = return CheckTxHash fromText "chainweb213Pact" = return Chainweb213Pact fromText "chainweb214Pact" = return Chainweb214Pact diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 2d3e3b2eeb..172c94126b 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -55,7 +55,7 @@ devnet = ChainwebVersion SPVBridge -> AllChains $ ForkAtBlockHeight $ BlockHeight 50 Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 80 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight $ BlockHeight 100 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 90 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 90 CheckTxHash -> AllChains $ ForkAtBlockHeight $ BlockHeight 110 Chainweb213Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 95 Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 115 diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index ac1280dc49..6b09cd773a 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -30,7 +30,7 @@ module Chainweb.Version.Guards , enablePactEvents , enableSPVBridge , pact4Coin3 - , pact420 + , pact42 , enforceKeysetFormats , doCheckTxHash , chainweb213Pact @@ -208,8 +208,8 @@ pact44NewTrans = checkFork atOrAfter Pact44NewTrans pact4Coin3 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool pact4Coin3 = checkFork after Pact4Coin3 -pact420 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool -pact420 = checkFork atOrAfter Pact420 +pact42 :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +pact42 = checkFork atOrAfter Pact42 chainweb213Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb213Pact = checkFork atOrAfter Chainweb213Pact diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 75494fb991..9cbda87313 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -129,7 +129,7 @@ mainnet = ChainwebVersion SPVBridge -> AllChains (ForkAtBlockHeight $ BlockHeight 1_275_000) Pact4Coin3 -> AllChains (ForkAtBlockHeight $ BlockHeight 1_722_500) -- 2021-06-19T03:34:05+00:00 EnforceKeysetFormats -> AllChains (ForkAtBlockHeight $ BlockHeight 2_162_000) -- 2022-01-17T17:51:12 - Pact420 -> AllChains (ForkAtBlockHeight $ BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 + Pact42 -> AllChains (ForkAtBlockHeight $ BlockHeight 2_334_500) -- 2022-01-17T17:51:12+00:00 CheckTxHash -> AllChains (ForkAtBlockHeight $ BlockHeight 2_349_800) -- 2022-01-23T02:53:38 Chainweb213Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_447_315) -- 2022-02-26T00:00:00+00:00 Chainweb214Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 2_605_663) -- 2022-04-22T00:00:00+00:00 diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index c6e198c052..a0e4349ce0 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -109,7 +109,7 @@ testnet = ChainwebVersion SPVBridge -> AllChains $ ForkAtBlockHeight $ BlockHeight 820_000 -- 2021-01-14T17:12:02 Pact4Coin3 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_261_000 -- 2021-06-17T15:54:14 EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_701_000 -- 2021-11-18T17:54:36 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_862_000 -- 2021-06-19T03:34:05 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_862_000 -- 2021-06-19T03:34:05 CheckTxHash -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_889_000 -- 2022-01-24T04:19:24 Chainweb213Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 1_974_556 -- 2022-02-25 00:00:00 Chainweb214Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 2_134_331 -- 2022-04-21T12:00:00Z diff --git a/test/Chainweb/Test/Pact/Checkpointer.hs b/test/Chainweb/Test/Pact/Checkpointer.hs index fa4ca9e189..2a4f1d4c11 100644 --- a/test/Chainweb/Test/Pact/Checkpointer.hs +++ b/test/Chainweb/Test/Pact/Checkpointer.hs @@ -505,7 +505,7 @@ runRegression pactdb e schemaInit = do let row' = RowData RDV1 $ ObjectMap $ M.fromList [("gah",toPV False),("fh",toPV (1 :: Int))] _writeRow pactdb Update usert "key1" row' conn assertEquals' "user update" (Just row') (_readRow pactdb usert "key1" conn) - let ks = mkKeySetText [PublicKeyText "skdjhfskj"] "predfun" + let ks = mkKeySet [PublicKeyText "skdjhfskj"] "predfun" _writeRow pactdb Write KeySets "ks1" ks conn assertEquals' "keyset write" (Just ks) $ _readRow pactdb KeySets "ks1" conn (modName,modRef,mod') <- loadModule diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index ba80fbe5c5..32431939b5 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -116,7 +116,7 @@ data PactTxTest = PactTxTest tests :: TestTree tests = testGroup testName [ test generousConfig freeGasModel "pact4coin3UpgradeTest" pact4coin3UpgradeTest - , test generousConfig freeGasModel "pact420UpgradeTest" pact420UpgradeTest + , test generousConfig freeGasModel "Pact42UpgradeTest" Pact42UpgradeTest , test generousConfig freeGasModel "minerKeysetTest" minerKeysetTest , test timeoutConfig freeGasModel "txTimeoutTest" txTimeoutTest , test generousConfig getGasModel "chainweb213Test" chainweb213Test @@ -165,7 +165,7 @@ minerKeysetTest = do where - badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySetText ["bad-bad-bad"] "keys-all" + badMiner = Miner (MinerId "miner") $ MinerKeys $ mkKeySet ["bad-bad-bad"] "keys-all" txTimeoutTest :: PactTestM () txTimeoutTest = do @@ -648,8 +648,8 @@ pact431UpgradeTest = do ]) -pact420UpgradeTest :: PactTestM () -pact420UpgradeTest = do +Pact42UpgradeTest :: PactTestM () +Pact42UpgradeTest = do -- run past genesis, upgrades runToHeight 3 @@ -1114,7 +1114,7 @@ pact4coin3UpgradeTest = do , PactTxTest badKeyset $ assertTxSuccess "Should allow bad keys" $ - pKeySet $ mkKeySetText ["badkey"] "keys-all" + pKeySet $ mkKeySet ["badkey"] "keys-all" ] assertTxEvents "Coinbase events @ block 7" [] =<< cbResult diff --git a/test/Chainweb/Test/Pact/RemotePactTest.hs b/test/Chainweb/Test/Pact/RemotePactTest.hs index 895041998d..192daf5539 100644 --- a/test/Chainweb/Test/Pact/RemotePactTest.hs +++ b/test/Chainweb/Test/Pact/RemotePactTest.hs @@ -881,7 +881,7 @@ allocationTest t cenv step = do tx3 = let c = "(define-keyset \"allocation02\" (read-keyset \"allocation02-keyset\"))" - d = mkKeySetText + d = mkKeySet ["0c8212a903f6442c84acd0069acc263c69434b5af37b2997b16d6348b53fcd0a"] "keys-all" in PactTransaction c $ Just (A.object [ "allocation02-keyset" A..= J.toJsonViaEncode d ]) diff --git a/test/Chainweb/Test/Pact/SPV.hs b/test/Chainweb/Test/Pact/SPV.hs index 2c49148e76..d99bfbef07 100644 --- a/test/Chainweb/Test/Pact/SPV.hs +++ b/test/Chainweb/Test/Pact/SPV.hs @@ -402,7 +402,7 @@ burnGen time pidv sid tid = do tx1Data = -- sender01 keyset guard - let ks = mkKeySetText + let ks = mkKeySet ["6be2f485a7af75fedb4b7f153a903f7e6000ca4aa501179c91a2450b777bd2a7"] "keys-all" diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 194bff07df..126a06c9c5 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -196,7 +196,7 @@ badMinerId :: MinerId badMinerId = MinerId "alpha\" (read-keyset \"miner-keyset\") 9999999.99)(coin.coinbase \"alpha" minerKeys0 :: MinerKeys -minerKeys0 = MinerKeys $ mkKeySetText +minerKeys0 = MinerKeys $ mkKeySet ["f880a433d6e2a13a32b6169030f56245efdd8c1b8a5027e9ce98a88e886bef27"] "default" @@ -267,7 +267,7 @@ testCoinbase797DateFix = testCaseSteps "testCoinbase791Fix" $ \step -> do miner = Miner (MinerId "tester01\" (read-keyset \"miner-keyset\") 1000.0)(coin.coinbase \"tester01") - (MinerKeys $ mkKeySetText ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") + (MinerKeys $ mkKeySet ["b67e109352e8e33c8fe427715daad57d35d25d025914dd705b97db35b1bfbaa5"] "keys-all") preForkHeight = 121451 postForkHeight = 121452 diff --git a/test/Chainweb/Test/Pact/Utils.hs b/test/Chainweb/Test/Pact/Utils.hs index 360bfbd106..3de29f6a46 100644 --- a/test/Chainweb/Test/Pact/Utils.hs +++ b/test/Chainweb/Test/Pact/Utils.hs @@ -237,7 +237,7 @@ mkKeySetData :: Key -> [SimpleKeyPair] -> Value mkKeySetData name keys = object [ name .= map fst keys ] sender00Ks :: KeySet -sender00Ks = mkKeySetText +sender00Ks = mkKeySet ["368820f80c324bbc7c2b0610688a7da43e39f91d118732671cd9c7500ff43cca"] "keys-all" diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index a57a793efc..3fc2b576bf 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -140,7 +140,7 @@ tests rdb = testGroup "Chainweb.Test.Rosetta.RestAPI" go , networkListTests , networkOptionsTests , networkStatusTests - , blockKAccountAfterPact420 + , blockKAccountAfterPact42 , constructionTransferTests , blockCoinV3RemediationTests ] @@ -177,8 +177,8 @@ accountBalanceTests tio envIo = -- TxLog parse error after fork to Pact 420. -- This assumes that this test occurs after the -- fork blockheight. -blockKAccountAfterPact420 :: RosettaTest -blockKAccountAfterPact420 tio envIo = +blockKAccountAfterPact42 :: RosettaTest +blockKAccountAfterPact42 tio envIo = testCaseSteps "Block k Account After Pact 420 Test" $ \step -> do cenv <- envIo rkmv <- newEmptyMVar @RequestKeys @@ -491,7 +491,7 @@ constructionTransferTests _ envIo = ks (TestKeySet _ Nothing pred') = P.mkKeySet [] pred' ks (TestKeySet _ (Just (pk,_)) pred') = - P.mkKeySetText [P.PublicKeyText pk] pred' + P.mkKeySet [P.PublicKeyText pk] pred' sender00KAcct = "k:" <> fst sender00 sender01KAcct = "k:" <> fst sender01 diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index a79066ae13..29cffaefcc 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -126,7 +126,7 @@ fastForks = tabulateHashMap $ \case Chainweb213Pact -> AllChains ForkAtGenesis PactEvents -> AllChains ForkAtGenesis CoinV2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 - Pact420 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 + Pact42 -> AllChains $ ForkAtBlockHeight $ BlockHeight 1 SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 ModuleNameFix -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 ModuleNameFix2 -> AllChains $ ForkAtBlockHeight $ BlockHeight 2 @@ -246,7 +246,7 @@ slowForkingCpmTestVersion g = buildTestVersion $ \v -> v SkipTxTimingValidation -> AllChains $ ForkAtBlockHeight (BlockHeight 2) ModuleNameFix -> AllChains $ ForkAtBlockHeight (BlockHeight 2) ModuleNameFix2 -> AllChains $ ForkAtBlockHeight (BlockHeight 2) - Pact420 -> AllChains $ ForkAtBlockHeight (BlockHeight 5) + Pact42 -> AllChains $ ForkAtBlockHeight (BlockHeight 5) CheckTxHash -> AllChains $ ForkAtBlockHeight (BlockHeight 7) EnforceKeysetFormats -> AllChains $ ForkAtBlockHeight (BlockHeight 10) PactEvents -> AllChains $ ForkAtBlockHeight (BlockHeight 10) @@ -275,4 +275,3 @@ noBridgeCpmTestVersion g = buildTestVersion $ \v -> v & cpmTestVersion g & versionName .~ ChainwebVersionName ("nobridge-CPM-" <> toText g) & versionForks .~ (fastForks & at SPVBridge ?~ AllChains ForkNever) - From 6d5a402f7503fdbf4cf155041f1ba31f467d6af2 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Thu, 9 Nov 2023 17:53:12 -0500 Subject: [PATCH 81/91] fix tests --- test/Chainweb/Test/Pact/PactMultiChainTest.hs | 6 +++--- test/Chainweb/Test/Rosetta/RestAPI.hs | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/Chainweb/Test/Pact/PactMultiChainTest.hs b/test/Chainweb/Test/Pact/PactMultiChainTest.hs index 32431939b5..95f6657141 100644 --- a/test/Chainweb/Test/Pact/PactMultiChainTest.hs +++ b/test/Chainweb/Test/Pact/PactMultiChainTest.hs @@ -116,7 +116,7 @@ data PactTxTest = PactTxTest tests :: TestTree tests = testGroup testName [ test generousConfig freeGasModel "pact4coin3UpgradeTest" pact4coin3UpgradeTest - , test generousConfig freeGasModel "Pact42UpgradeTest" Pact42UpgradeTest + , test generousConfig freeGasModel "pact42UpgradeTest" pact42UpgradeTest , test generousConfig freeGasModel "minerKeysetTest" minerKeysetTest , test timeoutConfig freeGasModel "txTimeoutTest" txTimeoutTest , test generousConfig getGasModel "chainweb213Test" chainweb213Test @@ -648,8 +648,8 @@ pact431UpgradeTest = do ]) -Pact42UpgradeTest :: PactTestM () -Pact42UpgradeTest = do +pact42UpgradeTest :: PactTestM () +pact42UpgradeTest = do -- run past genesis, upgrades runToHeight 3 diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 3fc2b576bf..93653b19b6 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -600,14 +600,17 @@ submitToConstructionAPI expectOps chainId' payer getKeys expectResult cenv step (Right (hsh :: P.PactHash)) <- pure $ fmap (P.fromUntypedHash . P.Hash . BS.toShort) (P.parseB16TextOnly $ _rosettaSigningPayload_hexBytes payload) - ED25519Sig sig <- P.signHash hsh kp + let + sig = case P.signHash hsh kp of + ED25519Sig s -> s + _ -> error "not an ED25519 signature" pure $! RosettaSignature { _rosettaSignature_signingPayload = payload , _rosettaSignature_publicKey = RosettaPublicKey pk CurveEdwards25519 , _rosettaSignature_signatureType = RosettaEd25519 - , _rosettaSignature_hexBytes = sig + , _rosettaSignature_hexBytes = P.toB16Text $ P.exportEd25519Signature sig } acct n = AccountId n Nothing Nothing From 90b8397ac71de8e0b95b366fecb03906f730156e Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Thu, 9 Nov 2023 15:07:04 -0800 Subject: [PATCH 82/91] Update git sha for pact --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index cfbba11863..96000f937b 100644 --- a/cabal.project +++ b/cabal.project @@ -64,7 +64,7 @@ source-repository-package type: git location: https://github.com/kadena-io/pact.git tag: 15d391406e84b65945021b4066ce0cf0464bcc63 - --sha256: BROKEN + --sha256: sha256-zuLPu8Ik8X/ur677uZ4Oz2I+YV3McAVnk1jpLE20CqI= source-repository-package type: git From 4d4e243a2300dfc5ee10c2d54e7bd433e2ec61f9 Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Thu, 9 Nov 2023 15:09:36 -0800 Subject: [PATCH 83/91] Update git sha for pact --- cabal.project | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/cabal.project b/cabal.project index 977d541889..96000f937b 100644 --- a/cabal.project +++ b/cabal.project @@ -63,13 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git -<<<<<<< HEAD - tag: 0e05fb9d4008a3d143e1842a88e9978ec8301276 - --sha256: 0zbjlrzmjzhbkw7kxrhqk360gx7a42qy2igffmsq88a3c8wzbg76 -======= tag: 15d391406e84b65945021b4066ce0cf0464bcc63 - --sha256: BROKEN ->>>>>>> mlep/webauthn-jwk-formats + --sha256: sha256-zuLPu8Ik8X/ur677uZ4Oz2I+YV3McAVnk1jpLE20CqI= source-repository-package type: git From 6f355d615f12a495f393849f557f311ca096202a Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Thu, 9 Nov 2023 15:56:53 -0800 Subject: [PATCH 84/91] supply argument to enforceKeyFormats --- src/Chainweb/Pact/TransactionExec.hs | 1 + src/Chainweb/Version/Guards.hs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index 5efeb6497c..9024ef9937 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -382,6 +382,7 @@ applyCoinbase v logger dbEnv (Miner mid mks@(MinerKeys mk)) reward@(ParsedDecima | fork1_3InEffect || enablePC = do when chainweb213Pact' $ enforceKeyFormats (\k -> throwM $ CoinbaseFailure $ "Invalid miner key: " <> sshow k) + (validKeyFormats v (ctxChainId txCtx) (ctxCurrentBlockHeight txCtx)) mk let (cterm, cexec) = mkCoinbaseTerm mid mks reward interp = Interpreter $ \_ -> do put initState; fmap pure (eval cterm) diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 6b09cd773a..e895581270 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -46,6 +46,7 @@ module Chainweb.Version.Guards , pactParserVersion , maxBlockGasLimit , validPPKSchemes + , validKeyFormats -- ** BlockHeader Validation Guards , slowEpochGuard @@ -57,6 +58,7 @@ module Chainweb.Version.Guards import Control.Lens import Numeric.Natural +import Pact.Types.KeySet (PublicKeyText, ed25519HexFormat, webauthnFormat) import Pact.Types.Scheme (PPKScheme(ED25519, WebAuthn)) import Chainweb.BlockHeight @@ -257,3 +259,9 @@ validPPKSchemes v cid bh = if chainweb221Pact v cid bh then [ED25519, WebAuthn] else [ED25519] + +validKeyFormats :: ChainwebVersion -> ChainId -> BlockHeight -> [PublicKeyText -> Bool] +validKeyFormats v cid bh = + if chainweb221Pact v cid bh + then [ed25519HexFormat, webauthnFormat] + else [ed25519HexFormat] From 979baf8cbe5150d2846286bb465a103abc4c800f Mon Sep 17 00:00:00 2001 From: Greg Hale Date: Thu, 9 Nov 2023 15:56:53 -0800 Subject: [PATCH 85/91] supply argument to enforceKeyFormats --- src/Chainweb/Pact/TransactionExec.hs | 1 + src/Chainweb/Version/Guards.hs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index 5efeb6497c..9024ef9937 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -382,6 +382,7 @@ applyCoinbase v logger dbEnv (Miner mid mks@(MinerKeys mk)) reward@(ParsedDecima | fork1_3InEffect || enablePC = do when chainweb213Pact' $ enforceKeyFormats (\k -> throwM $ CoinbaseFailure $ "Invalid miner key: " <> sshow k) + (validKeyFormats v (ctxChainId txCtx) (ctxCurrentBlockHeight txCtx)) mk let (cterm, cexec) = mkCoinbaseTerm mid mks reward interp = Interpreter $ \_ -> do put initState; fmap pure (eval cterm) diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index 6b09cd773a..e895581270 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -46,6 +46,7 @@ module Chainweb.Version.Guards , pactParserVersion , maxBlockGasLimit , validPPKSchemes + , validKeyFormats -- ** BlockHeader Validation Guards , slowEpochGuard @@ -57,6 +58,7 @@ module Chainweb.Version.Guards import Control.Lens import Numeric.Natural +import Pact.Types.KeySet (PublicKeyText, ed25519HexFormat, webauthnFormat) import Pact.Types.Scheme (PPKScheme(ED25519, WebAuthn)) import Chainweb.BlockHeight @@ -257,3 +259,9 @@ validPPKSchemes v cid bh = if chainweb221Pact v cid bh then [ED25519, WebAuthn] else [ED25519] + +validKeyFormats :: ChainwebVersion -> ChainId -> BlockHeight -> [PublicKeyText -> Bool] +validKeyFormats v cid bh = + if chainweb221Pact v cid bh + then [ed25519HexFormat, webauthnFormat] + else [ed25519HexFormat] From 87eaeead5f93705f280ea523f1c66736fc0c1003 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 10 Nov 2023 06:15:57 -0500 Subject: [PATCH 86/91] Fix cwtool --- tools/cwtool/TxSimulator.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cwtool/TxSimulator.hs b/tools/cwtool/TxSimulator.hs index 4b13a8b6d2..e775ade0fb 100644 --- a/tools/cwtool/TxSimulator.hs +++ b/tools/cwtool/TxSimulator.hs @@ -114,7 +114,7 @@ simulate sc@(SimConfig dbDir txIdx' _ _ cid ver gasLog doTypecheck) = do miner <- decodeStrictOrThrow $ _minerData md let Transaction tx = fst $ txs V.! txIdx cmdTx <- decodeStrictOrThrow tx - case validateCommand cmdTx of + case validateCommand ver cid cmdTx of Left _ -> error "bad cmd" Right cmdPwt -> do let cmd = payloadObj <$> cmdPwt From 5abe334a8ce312f47472d0ad4cd4a5394d25a2cd Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 10 Nov 2023 06:20:42 -0500 Subject: [PATCH 87/91] Add chainweb222Pact fork --- src/Chainweb/Pact/TransactionExec.hs | 4 ++++ src/Chainweb/Version.hs | 3 +++ src/Chainweb/Version/Development.hs | 1 + src/Chainweb/Version/Guards.hs | 4 ++++ src/Chainweb/Version/Mainnet.hs | 1 + src/Chainweb/Version/Testnet.hs | 1 + test/Chainweb/Test/TestVersions.hs | 2 ++ 7 files changed, 16 insertions(+) diff --git a/src/Chainweb/Pact/TransactionExec.hs b/src/Chainweb/Pact/TransactionExec.hs index 9024ef9937..fbe3646ec4 100644 --- a/src/Chainweb/Pact/TransactionExec.hs +++ b/src/Chainweb/Pact/TransactionExec.hs @@ -356,6 +356,7 @@ flagsFor v cid bh = S.fromList $ concat , enablePact48 v cid bh , disableReturnRTC v cid bh , enablePact49 v cid bh + , enablePact410 v cid bh ] applyCoinbase @@ -785,6 +786,9 @@ enablePact48 v cid bh = [FlagDisablePact48 | not (chainweb220Pact v cid bh)] enablePact49 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] enablePact49 v cid bh = [FlagDisablePact49 | not (chainweb221Pact v cid bh)] +enablePact410 :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] +enablePact410 v cid bh = [FlagDisablePact410 | not (chainweb222Pact v cid bh)] + -- | Even though this is not forking, abstracting for future shutoffs disableReturnRTC :: ChainwebVersion -> V.ChainId -> BlockHeight -> [ExecutionFlag] disableReturnRTC _v _cid _bh = [FlagDisableRuntimeReturnTypeChecking] diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 2e0cadb84e..ebef221327 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -191,6 +191,7 @@ data Fork | Chainweb219Pact | Chainweb220Pact | Chainweb221Pact + | Chainweb222Pact -- always add new forks at the end, not in the middle of the constructors. deriving stock (Bounded, Generic, Eq, Enum, Ord, Show) deriving anyclass (NFData, Hashable) @@ -222,6 +223,7 @@ instance HasTextRepresentation Fork where toText Chainweb219Pact = "chainweb219Pact" toText Chainweb220Pact = "chainweb220Pact" toText Chainweb221Pact = "chainweb221Pact" + toText Chainweb222Pact = "chainweb222Pact" fromText "slowEpoch" = return SlowEpoch fromText "vuln797Fix" = return Vuln797Fix @@ -249,6 +251,7 @@ instance HasTextRepresentation Fork where fromText "chainweb219Pact" = return Chainweb219Pact fromText "chainweb220Pact" = return Chainweb220Pact fromText "chainweb221Pact" = return Chainweb221Pact + fromText "chainweb222Pact" = return Chainweb222Pact fromText t = throwM . TextFormatException $ "Unknown Chainweb fork: " <> t instance ToJSON Fork where diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 172c94126b..843a0b1b18 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -67,6 +67,7 @@ devnet = ChainwebVersion Chainweb219Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 550 Chainweb220Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 560 Chainweb221Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 580 + Chainweb222Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 600 , _versionUpgrades = foldr (chainZip HM.union) (AllChains mempty) [ forkUpgrades devnet diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index e895581270..20cc033abc 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -42,6 +42,7 @@ module Chainweb.Version.Guards , chainweb219Pact , chainweb220Pact , chainweb221Pact + , chainweb222Pact , pact44NewTrans , pactParserVersion , maxBlockGasLimit @@ -240,6 +241,9 @@ chainweb220Pact = checkFork atOrAfter Chainweb220Pact chainweb221Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool chainweb221Pact = checkFork atOrAfter Chainweb221Pact +chainweb222Pact :: ChainwebVersion -> ChainId -> BlockHeight -> Bool +chainweb222Pact = checkFork atOrAfter Chainweb222Pact + pactParserVersion :: ChainwebVersion -> ChainId -> BlockHeight -> PactParserVersion pactParserVersion v cid bh | chainweb213Pact v cid bh = PactParserChainweb213 diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index 9cbda87313..a0b9c75cf4 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -141,6 +141,7 @@ mainnet = ChainwebVersion Chainweb219Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 3_774_423) -- 2023-06-02 00:00:00+00:00 Chainweb220Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 4_056_499) -- 2023-09-08 00:00:00+00:00 Chainweb221Pact -> AllChains (ForkAtBlockHeight $ BlockHeight 4_177_889) -- 2023-10-20 00:00:00+00:00 + Chainweb222Pact -> AllChains ForkNever -- for chainweb 2.22 , _versionGraphs = (to20ChainsMainnet, twentyChainGraph) `Above` diff --git a/src/Chainweb/Version/Testnet.hs b/src/Chainweb/Version/Testnet.hs index a0e4349ce0..1633e05ac6 100644 --- a/src/Chainweb/Version/Testnet.hs +++ b/src/Chainweb/Version/Testnet.hs @@ -121,6 +121,7 @@ testnet = ChainwebVersion Chainweb219Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 3_299_753 -- 2023-06-01 12:00:00+00:00 Chainweb220Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 3_580_964 -- 2023-09-08 12:00:00+00:00 Chainweb221Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 3_702_250 -- 2023-10-19 12:00:00+00:00 + Chainweb222Pact -> AllChains ForkNever -- for chainweb 2.22 , _versionGraphs = (to20ChainsTestnet, twentyChainGraph) `Above` diff --git a/test/Chainweb/Test/TestVersions.hs b/test/Chainweb/Test/TestVersions.hs index 29cffaefcc..0e02c5227f 100644 --- a/test/Chainweb/Test/TestVersions.hs +++ b/test/Chainweb/Test/TestVersions.hs @@ -139,6 +139,7 @@ fastForks = tabulateHashMap $ \case Chainweb219Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 27 Chainweb220Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 30 Chainweb221Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 33 + Chainweb222Pact -> AllChains $ ForkAtBlockHeight $ BlockHeight 35 -- | A test version without Pact or PoW, with only one chain graph. barebonesTestVersion :: ChainGraph -> ChainwebVersion @@ -260,6 +261,7 @@ slowForkingCpmTestVersion g = buildTestVersion $ \v -> v Chainweb219Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 71) Chainweb220Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 85) Chainweb221Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 100) + Chainweb222Pact -> AllChains $ ForkAtBlockHeight (BlockHeight 110) -- | CPM version (see `cpmTestVersion`) with forks and upgrades quickly enabled. fastForkingCpmTestVersion :: ChainGraph -> ChainwebVersion From a075f102cc83b67349a62492315dd93a414c8746 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Fri, 10 Nov 2023 06:37:34 -0500 Subject: [PATCH 88/91] Fix tests --- src/Chainweb/Version.hs | 4 ++-- test/Chainweb/Test/Pact/TransactionTests.hs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index ebef221327..34a5becd44 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -211,7 +211,7 @@ instance HasTextRepresentation Fork where toText SPVBridge = "spvBridge" toText Pact4Coin3 = "pact4Coin3" toText EnforceKeysetFormats = "enforceKeysetFormats" - toText Pact42 = "Pact42" + toText Pact42 = "pact42" toText CheckTxHash = "checkTxHash" toText Chainweb213Pact = "chainweb213Pact" toText Chainweb214Pact = "chainweb214Pact" @@ -239,7 +239,7 @@ instance HasTextRepresentation Fork where fromText "spvBridge" = return SPVBridge fromText "pact4Coin3" = return Pact4Coin3 fromText "enforceKeysetFormats" = return EnforceKeysetFormats - fromText "Pact42" = return Pact42 + fromText "pact42" = return Pact42 fromText "checkTxHash" = return CheckTxHash fromText "chainweb213Pact" = return Chainweb213Pact fromText "chainweb214Pact" = return Chainweb214Pact diff --git a/test/Chainweb/Test/Pact/TransactionTests.hs b/test/Chainweb/Test/Pact/TransactionTests.hs index 63d8b9d770..126a06c9c5 100644 --- a/test/Chainweb/Test/Pact/TransactionTests.hs +++ b/test/Chainweb/Test/Pact/TransactionTests.hs @@ -124,8 +124,8 @@ tests = testGroup "Chainweb.Test.Pact.TransactionTests" , testGroup "Coinbase Vuln Fix Tests" [ testCoinbase797DateFix , testCase "testCoinbaseEnforceFailure" testCoinbaseEnforceFailure - , testCase "testCoinbaseUpgradeDevnet0" (testCoinbaseUpgradeDevnet (unsafeChainId 0) 1) - , testCase "testCoinbaseUpgradeDevnet1" (testCoinbaseUpgradeDevnet (unsafeChainId 1) 1) + , testCase "testCoinbaseUpgradeDevnet0" (testCoinbaseUpgradeDevnet (unsafeChainId 0) 3) + , testCase "testCoinbaseUpgradeDevnet1" (testCoinbaseUpgradeDevnet (unsafeChainId 1) 4) ] , testGroup "20-Chain Fork Upgrade Tests" [ testTwentyChainDevnetUpgrades From dd933433babaa717dbb50134190cc94fddb2ae41 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 13 Nov 2023 09:47:04 -0500 Subject: [PATCH 89/91] Update pact --- cabal.project | 4 ++-- src/Chainweb/Rosetta/Utils.hs | 6 ++---- src/Chainweb/Version/Guards.hs | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cabal.project b/cabal.project index 96000f937b..4926f0218b 100644 --- a/cabal.project +++ b/cabal.project @@ -63,8 +63,8 @@ package yet-another-logger source-repository-package type: git location: https://github.com/kadena-io/pact.git - tag: 15d391406e84b65945021b4066ce0cf0464bcc63 - --sha256: sha256-zuLPu8Ik8X/ur677uZ4Oz2I+YV3McAVnk1jpLE20CqI= + tag: 4995af9c645ecfd442b803b70ab519f31e88b361 + --sha256: 1pl24v4yjk1sq69h62vhi99dl07n0invss14w9jd0j50mxcayxxr source-repository-package type: git diff --git a/src/Chainweb/Rosetta/Utils.hs b/src/Chainweb/Rosetta/Utils.hs index b3101af880..38d16a86f1 100644 --- a/src/Chainweb/Rosetta/Utils.hs +++ b/src/Chainweb/Rosetta/Utils.hs @@ -36,7 +36,6 @@ import qualified Data.Text.Encoding as T import qualified Pact.Types.Runtime as P import qualified Pact.Types.RPC as P import qualified Pact.Types.Command as P -import qualified Pact.Types.Crypto as P import qualified Pact.Parse as P import qualified Data.Set as S import Data.Maybe ( fromMaybe ) @@ -785,16 +784,15 @@ matchSigs sigs signers = do $ HM.lookup addr m sigAndAddr (RosettaSignature _ (RosettaPublicKey pk ct) sigTyp sig) = do - sigDecoded <- toRosettaError RosettaInvalidSignature $! P.parseB16TextOnly sig sigScheme <- sigToScheme sigTyp pkScheme <- getScheme ct when (sigScheme /= pkScheme) (Left $ stringRosettaError RosettaInvalidSignature $ "Expected the same Signature and PublicKey type for Signature=" ++ show sig) - userSig <- toRosettaError RosettaInvalidSignature $! P.parseEd25519Signature sigDecoded + let userSig = P.ED25519Sig sig addr <- toPactPubKeyAddr pk - pure (addr, P.ED25519Sig userSig) + pure (addr, userSig) -------------------------------------------------------------------------------- -- Rosetta Helper Types -- diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index e895581270..d790090798 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -58,7 +58,7 @@ module Chainweb.Version.Guards import Control.Lens import Numeric.Natural -import Pact.Types.KeySet (PublicKeyText, ed25519HexFormat, webauthnFormat) +import Pact.Types.KeySet (PublicKeyText, ed25519HexFormat, webAuthnFormat) import Pact.Types.Scheme (PPKScheme(ED25519, WebAuthn)) import Chainweb.BlockHeight @@ -263,5 +263,5 @@ validPPKSchemes v cid bh = validKeyFormats :: ChainwebVersion -> ChainId -> BlockHeight -> [PublicKeyText -> Bool] validKeyFormats v cid bh = if chainweb221Pact v cid bh - then [ed25519HexFormat, webauthnFormat] + then [ed25519HexFormat, webAuthnFormat] else [ed25519HexFormat] From 92c1c755db1ff888dd6f5f0545964a2af9d0c3b2 Mon Sep 17 00:00:00 2001 From: Edmund Noble Date: Mon, 13 Nov 2023 09:54:40 -0500 Subject: [PATCH 90/91] Fix tests --- test/Chainweb/Test/Rosetta/RestAPI.hs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/Chainweb/Test/Rosetta/RestAPI.hs b/test/Chainweb/Test/Rosetta/RestAPI.hs index 93653b19b6..9eb8f2afee 100644 --- a/test/Chainweb/Test/Rosetta/RestAPI.hs +++ b/test/Chainweb/Test/Rosetta/RestAPI.hs @@ -601,16 +601,14 @@ submitToConstructionAPI expectOps chainId' payer getKeys expectResult cenv step (P.fromUntypedHash . P.Hash . BS.toShort) (P.parseB16TextOnly $ _rosettaSigningPayload_hexBytes payload) let - sig = case P.signHash hsh kp of - ED25519Sig s -> s - _ -> error "not an ED25519 signature" + sig = P.signHash hsh kp pure $! RosettaSignature { _rosettaSignature_signingPayload = payload , _rosettaSignature_publicKey = RosettaPublicKey pk CurveEdwards25519 , _rosettaSignature_signatureType = RosettaEd25519 - , _rosettaSignature_hexBytes = P.toB16Text $ P.exportEd25519Signature sig + , _rosettaSignature_hexBytes = sig } acct n = AccountId n Nothing Nothing From e6544a01506df46559c8db32459534de095ef8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enis=20Bayramo=C4=9Flu?= Date: Mon, 13 Nov 2023 16:26:00 +0100 Subject: [PATCH 91/91] Update cabal.project --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index 4926f0218b..0a453d0086 100644 --- a/cabal.project +++ b/cabal.project @@ -64,7 +64,7 @@ source-repository-package type: git location: https://github.com/kadena-io/pact.git tag: 4995af9c645ecfd442b803b70ab519f31e88b361 - --sha256: 1pl24v4yjk1sq69h62vhi99dl07n0invss14w9jd0j50mxcayxxr + --sha256: sha256-ErPqFFGcPzSYFf8D22RWCSTQM3Kej7MDWZLh27nk19E= source-repository-package type: git