diff --git a/docs/en/pact-functions.md b/docs/en/pact-functions.md index e731c0cf7..7697d28f2 100644 --- a/docs/en/pact-functions.md +++ b/docs/en/pact-functions.md @@ -1798,19 +1798,21 @@ pact> (scalar-mult 'g1 {'x: 1, 'y: 2} 2) ## Hashes {#Hashes} -### keccak256 {#keccak256} +### hash-keccak256 {#hash-keccak256} -*chunks* `[string]` *→* `string` +*bytes* `[string]` *→* `string` -Compute the hash of a list of base64-encoded inputs. The hash is computed incrementally over all of the base64-decoded inputs. +Compute the hash of a list of unpadded base64url-encoded inputs. The hash is computed incrementally over all of the decoded inputs. ```lisp -pact> (keccak256 [""]) -"xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=" -pact> (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk/ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY+9BOzuztZI869P2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli+P5g2lv+m0JXGNptNr3nppdMTYikSj462PBK56fp4r/ej6eGaYgIkk80Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG/L84CrdVjE/ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w=="]) -"DsjZ0g3fCnsCUelBpyYbVXUH/2KHtQQ2Ko8XNMWpEBI=" -pact> (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaM=", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA=", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE=", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU=", "pCM4e4FRtBM+0j9tcYe1DsIgStkBrXTTluRCdODsr6o=", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08=", "2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA=", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj+YNpb/ptCVw=", "Y2m02veeml0xNiKRKPjrY8Ernp+niv96Pp4ZpiAiSTw=", "0Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDA=", "dOXaWHBAp2l11+OfRJSs5Uhvy/OAq3VYxP6JZWM1uC4=", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4=", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmw=", "4vKnl+SdwfE1MZgB+m85awb5deKhkKAj5HS2GOc="]) -"DsjZ0g3fCnsCUelBpyYbVXUH/2KHtQQ2Ko8XNMWpEBI=" +pact> (hash-keccak256 []) +"xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e_rYBF2FpHA" +pact> (hash-keccak256 [""]) +"xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e_rYBF2FpHA" +pact> (hash-keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm-tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS-yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN_4Ir7DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylqnVqSpgt-tvzHvJVCDxLXGwbmaEH19Ov_9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji6L9Mdt4I4_xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ-u1Dyk7tCo3XYbx-292iiih8YM1Cr1-cdY5cclAjHAmlglY2ia_GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhDpYvZzyCxek1F4aoo7_VcAMYV71Mh_T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL_YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q"]) +"DqM-LjT1ckQGQCRMfx9fBGl86XE5vacqZVjYZjwCs4g" +pact> (hash-keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9g", "D5vralAgdYir887ujHfNilrUip4KoHTtOIc4NiSW0vs", "LIdUO7M0nqZJl84-e0JOqS0SL1fbsIVagDBYQ3_givs", "DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylo", "p1akqYLfrb8x7yVQg8S1xsG5mhB9fTr__biRR8LMTJo", "JkP0eOXi05Ouo3tMfLS16X2tzxa2tQquDztUns5HdG0", "ts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji4", "i_THbeCOP8RqoCH5icSTKcesrFpohVbXvLyypdS-adM", "KE6cQOxIOO6FkhIM4goLY17K2qhP1WkFCfVPd-NaQXw", "WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ8", "rtQ8pO7QqN12G8ftvdoooofGDNQq9fnHWOXHJQIxwJo", "WCVjaJr8ZeK3mnoraCAGZ3UukQF0bwMYTiOZ5O2INcs", "jprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhA", "6WL2c8gsXpNReGqKO_1XADGFe9TIf0_KMe1NUOFPIQc", "2gLLUFhwC3TqJBqLQdeEYWWPGyuQv9hKTCydZUOGGrM", "xWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q"]) +"DqM-LjT1ckQGQCRMfx9fBGl86XE5vacqZVjYZjwCs4g" ``` diff --git a/golden/gas-model/golden b/golden/gas-model/golden index af81757ea..7c41279ca 100644 --- a/golden/gas-model/golden +++ b/golden/gas-model/golden @@ -126,6 +126,13 @@ - 5 - - (hash smallOjectMap) - 5 +- - |- + (hash-keccak256 []) + (hash-keccak256 [""]) + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk_ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY-9BOzuztZI869P2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli-P5g2lv-m0JXGNptNr3nppdMTYikSj462PBK56fp4r_ej6eGaYgIkk80Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG_L84CrdVjE_ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w"]) + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaM", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU", "pCM4e4FRtBM-0j9tcYe1DsIgStkBrXTTluRCdODsr6o", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08", "2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj-YNpb_ptCVw", "Y2m02veeml0xNiKRKPjrY8Ernp-niv96Pp4ZpiAiSTw", "0Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDA", "dOXaWHBAp2l11-OfRJSs5Uhvy_OAq3VYxP6JZWM1uC4", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmw", "4vKnl-SdwfE1MZgB-m85awb5deKhkKAj5HS2GOc"]) + (hash-keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm-tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS-yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN_4Ir7DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylqnVqSpgt-tvzHvJVCDxLXGwbmaEH19Ov_9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji6L9Mdt4I4_xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ-u1Dyk7tCo3XYbx-292iiih8YM1Cr1-cdY5cclAjHAmlglY2ia_GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhDpYvZzyCxek1F4aoo7_VcAMYV71Mh_T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL_YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q"]) + - 44 - - (* longNumber longNumber) - 3 - - (* medNumber medNumber) @@ -922,12 +929,6 @@ (diff-time (time "2016-07-22T12:00:00Z") (time "2018-07-22T12:00:00Z")) - 12 -- - |- - (keccak256 [""]) - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk/ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY+9BOzuztZI869P2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli+P5g2lv+m0JXGNptNr3nppdMTYikSj462PBK56fp4r/ej6eGaYgIkk80Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG/L84CrdVjE/ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w=="]) - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaM=", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA=", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE=", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU=", "pCM4e4FRtBM+0j9tcYe1DsIgStkBrXTTluRCdODsr6o=", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08=", "2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA=", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj+YNpb/ptCVw=", "Y2m02veeml0xNiKRKPjrY8Ernp+niv96Pp4ZpiAiSTw=", "0Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDA=", "dOXaWHBAp2l11+OfRJSs5Uhvy/OAq3VYxP6JZWM1uC4=", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4=", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmw=", "4vKnl+SdwfE1MZgB+m85awb5deKhkKAj5HS2GOc="]) - (keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm+tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS+yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN/4Ir7DItecXm5BEu/TYGnFjsxOeMIiLU2sPlX7/macWL0ylqnVqSpgt+tvzHvJVCDxLXGwbmaEH19Ov/9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo+N0fy+taji6L9Mdt4I4/xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge/qwA46QKV0LE5AlMKgw0cK91T8fnJ+u1Dyk7tCo3XYbx+292iiih8YM1Cr1+cdY5cclAjHAmlglY2ia/GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ/gvQvMYPekpf8/cMXtTenIxRmhDpYvZzyCxek1F4aoo7/VcAMYV71Mh/T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL/YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q=="]) - - 14 - - (make-list longNumber true) - 1026 - - (make-list medNumber true) diff --git a/src-tool/Pact/Analyze/Eval/Core.hs b/src-tool/Pact/Analyze/Eval/Core.hs index bfc5755c1..220883664 100644 --- a/src-tool/Pact/Analyze/Eval/Core.hs +++ b/src-tool/Pact/Analyze/Eval/Core.hs @@ -51,7 +51,6 @@ import Crypto.Hash.Keccak256Native import qualified Data.ByteString as BS import Data.Functor ((<&>)) import qualified Data.Vector as V -import Data.Default import qualified Pact.JSON.Encode as J @@ -315,15 +314,15 @@ evalCore (ListHash ty' xs) = do _ -> throwErrorNoLoc (FailureMessage "Unsupported type, currently we support integer, decimal, string, and bool") evalCore (Keccak256Hash xs) = eval xs <&> unliteralS >>= \case - Nothing ->do + Nothing -> do -- (keccak256 []) - let h = "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=" + let h = "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA" emitWarning (FVShimmedStaticContent "keccac256" ("of type '[string]', substitute '" <> T.pack h <> "')")) pure (literalS (Str h)) Just (xs':: [Str]) -> do - let tm = fmap (\x -> Pact.TLiteral (Pact.LString $ T.pack $ unStr x) def) xs' + let tm = fmap (\x -> T.pack (unStr x)) xs' h = keccak256 (V.fromList tm) - pure (literalS . Str . T.unpack $ h) + pure (literalS . Str . either (error . show) T.unpack $ h) --T.unpack $ h) evalCore (ListContains ty needle haystack) = withSymVal ty $ do S _ needle' <- withSing ty $ eval needle diff --git a/src/Crypto/Hash/Keccak256Native.hs b/src/Crypto/Hash/Keccak256Native.hs index adc8ea3a5..4ea05b2f0 100644 --- a/src/Crypto/Hash/Keccak256Native.hs +++ b/src/Crypto/Hash/Keccak256Native.hs @@ -1,4 +1,5 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE OverloadedStrings #-} @@ -6,18 +7,16 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} --- | Implementation of the `keccack256` pact native. +-- | Implementation of the `keccak256` pact native. -- -- `keccak256` takes as input a Pact object representing a -- 'HyperlaneMessage', and returns a base16-encoded hash of the abi-encoding -- of the input. -module Crypto.Hash.Keccak256Native (keccak256) where +module Crypto.Hash.Keccak256Native (Keccak256Error(..), keccak256) where import Control.Exception (Exception(..), SomeException(..), try) -import Control.Lens ((^.)) import Control.Monad (forM_) import Control.Monad.Catch (throwM) -import Data.ByteString.Base64 qualified as Base64 import Data.ByteString.Short qualified as BSS import Data.Hash.Class.Mutable (initialize, finalize, updateByteString) import Data.Hash.Internal.OpenSSL (OpenSslException(..)) @@ -25,33 +24,31 @@ import Data.Hash.Keccak (Keccak256(..)) import Data.Text (Text) import Data.Text.Encoding qualified as Text import Data.Vector (Vector) -import Pact.Types.Term (Term, Name, _TLitString) +import Pact.Types.Util (encodeBase64UrlUnpadded, decodeBase64UrlUnpadded) import System.IO.Unsafe (unsafePerformIO) -keccak256 :: Vector (Term Name) -> Text +data Keccak256Error + = Keccak256OpenSslException String + | Keccak256Base64Exception String + deriving stock (Eq, Show) + deriving anyclass (Exception) + +keccak256 :: Vector Text -> Either Keccak256Error Text keccak256 strings = unsafePerformIO $ do e <- try @SomeException @_ $ do ctx <- initialize @Keccak256 forM_ strings $ \string -> do - let s = string ^. _TLitString - case Base64.decode (Text.encodeUtf8 s) of + case decodeBase64UrlUnpadded (Text.encodeUtf8 string) of Left b64Err -> do - throwM (Base64Exception b64Err) + throwM (Keccak256Base64Exception b64Err) Right bytes -> do updateByteString @Keccak256 ctx bytes Keccak256 hash <- finalize ctx pure (BSS.fromShort hash) case e of Left err - | Just (OpenSslException _) <- fromException err -> error "keccak256 failed" - | Just (Base64Exception _) <- fromException err -> error "keccak256 failed: invalid base64 input" + | Just (OpenSslException msg) <- fromException err -> pure (Left (Keccak256OpenSslException msg)) + | Just (exc :: Keccak256Error) <- fromException err -> pure (Left exc) | otherwise -> error "keccak256 failed" - Right hash -> pure (Text.decodeUtf8 (Base64.encode hash)) + Right hash -> pure (Right (Text.decodeUtf8 (encodeBase64UrlUnpadded hash))) {-# noinline keccak256 #-} - -newtype Base64Exception = Base64Exception String - deriving stock (Show) - -instance Exception Base64Exception where - displayException (Base64Exception err) = - "base64 decode exception: " ++ err diff --git a/src/Pact/Gas/Table.hs b/src/Pact/Gas/Table.hs index 6a3b17b9f..facc91534 100644 --- a/src/Pact/Gas/Table.hs +++ b/src/Pact/Gas/Table.hs @@ -58,6 +58,7 @@ data GasCostConfig = GasCostConfig , _gasCostConfig_hyperlaneMessageIdGasPerRecipientOneHundredBytes :: MilliGas , _gasCostConfig_hyperlaneDecodeTokenMessageGasPerOneHundredBytes :: MilliGas , _gasCostConfig_keccak256GasPerOneHundredBytes :: MilliGas + , _gasCostConfig_keccak256GasPerChunk :: MilliGas } defaultGasConfig :: GasCostConfig @@ -87,6 +88,7 @@ defaultGasConfig = GasCostConfig , _gasCostConfig_hyperlaneMessageIdGasPerRecipientOneHundredBytes = MilliGas 47 , _gasCostConfig_hyperlaneDecodeTokenMessageGasPerOneHundredBytes = MilliGas 50 , _gasCostConfig_keccak256GasPerOneHundredBytes = MilliGas 146 + , _gasCostConfig_keccak256GasPerChunk = MilliGas 2_120 } defaultGasTable :: Map Text Gas @@ -244,7 +246,7 @@ defaultGasTable = ,("poseidon-hash-hack-a-chain", 124) ,("hyperlane-message-id", 2) ,("hyperlane-decode-token-message", 2) - ,("keccak256",3) + ,("hash-keccak256",1) ] {-# NOINLINE defaultGasTable #-} @@ -348,9 +350,12 @@ tableGasModel gasConfig = GHyperlaneDecodeTokenMessage len -> let MilliGas costPerOneHundredBytes = _gasCostConfig_hyperlaneDecodeTokenMessageGasPerOneHundredBytes gasConfig in MilliGas (costPerOneHundredBytes * div (fromIntegral len) 100) - GKeccak256 numBytes -> + GKeccak256 numBytesInChunk -> let MilliGas costPerOneHundredBytes = _gasCostConfig_keccak256GasPerOneHundredBytes gasConfig - in MilliGas (costPerOneHundredBytes * div (fromIntegral numBytes) 100) + MilliGas costPerChunk = _gasCostConfig_keccak256GasPerChunk gasConfig + -- we need to use ceiling here, otherwise someone could cheat by + -- having as many bytes as they want, but in chunks of 99 bytes. + in MilliGas (costPerChunk + costPerOneHundredBytes * ceiling (fromIntegral @_ @Double numBytesInChunk / 100.0)) in GasModel { gasModelName = "table" diff --git a/src/Pact/GasModel/GasTests.hs b/src/Pact/GasModel/GasTests.hs index c58c6b384..43216e5d9 100644 --- a/src/Pact/GasModel/GasTests.hs +++ b/src/Pact/GasModel/GasTests.hs @@ -228,7 +228,7 @@ allTests = HM.fromList -- SPI/Hyperlane , ("hyperlane-message-id", hyperlaneMessageIdTests) , ("enforce-verifier", enforceVerifierTests) - , ("keccak256", keccak256Tests) + , ("hash-keccak256", keccak256Tests) -- Non-native concepts to benchmark , ("use", useTests) @@ -2050,8 +2050,9 @@ keccak256Tests :: NativeDefName -> GasUnitTests keccak256Tests = defGasUnitTest $ PactExpression keccak256ExprText Nothing where keccak256ExprText = [text| - (keccak256 [""]) - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk/ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY+9BOzuztZI869P2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli+P5g2lv+m0JXGNptNr3nppdMTYikSj462PBK56fp4r/ej6eGaYgIkk80Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG/L84CrdVjE/ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w=="]) - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaM=", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA=", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE=", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU=", "pCM4e4FRtBM+0j9tcYe1DsIgStkBrXTTluRCdODsr6o=", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08=", "2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA=", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj+YNpb/ptCVw=", "Y2m02veeml0xNiKRKPjrY8Ernp+niv96Pp4ZpiAiSTw=", "0Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDA=", "dOXaWHBAp2l11+OfRJSs5Uhvy/OAq3VYxP6JZWM1uC4=", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4=", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmw=", "4vKnl+SdwfE1MZgB+m85awb5deKhkKAj5HS2GOc="]) - (keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm+tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS+yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN/4Ir7DItecXm5BEu/TYGnFjsxOeMIiLU2sPlX7/macWL0ylqnVqSpgt+tvzHvJVCDxLXGwbmaEH19Ov/9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo+N0fy+taji6L9Mdt4I4/xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge/qwA46QKV0LE5AlMKgw0cK91T8fnJ+u1Dyk7tCo3XYbx+292iiih8YM1Cr1+cdY5cclAjHAmlglY2ia/GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ/gvQvMYPekpf8/cMXtTenIxRmhDpYvZzyCxek1F4aoo7/VcAMYV71Mh/T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL/YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q=="]) + (hash-keccak256 []) + (hash-keccak256 [""]) + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk_ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY-9BOzuztZI869P2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli-P5g2lv-m0JXGNptNr3nppdMTYikSj462PBK56fp4r_ej6eGaYgIkk80Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG_L84CrdVjE_ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w"]) + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaM", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU", "pCM4e4FRtBM-0j9tcYe1DsIgStkBrXTTluRCdODsr6o", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08", "2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj-YNpb_ptCVw", "Y2m02veeml0xNiKRKPjrY8Ernp-niv96Pp4ZpiAiSTw", "0Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDA", "dOXaWHBAp2l11-OfRJSs5Uhvy_OAq3VYxP6JZWM1uC4", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmw", "4vKnl-SdwfE1MZgB-m85awb5deKhkKAj5HS2GOc"]) + (hash-keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm-tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS-yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN_4Ir7DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylqnVqSpgt-tvzHvJVCDxLXGwbmaEH19Ov_9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji6L9Mdt4I4_xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ-u1Dyk7tCo3XYbx-292iiih8YM1Cr1-cdY5cclAjHAmlglY2ia_GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhDpYvZzyCxek1F4aoo7_VcAMYV71Mh_T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL_YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q"]) |] diff --git a/src/Pact/Interpreter.hs b/src/Pact/Interpreter.hs index 5d73bbc9e..97eb772e6 100644 --- a/src/Pact/Interpreter.hs +++ b/src/Pact/Interpreter.hs @@ -282,7 +282,7 @@ pact411Natives :: [Text] pact411Natives = ["enforce-verifier", "hyperlane-message-id", "hyperlane-decode-token-message"] pact412Natives :: [Text] -pact412Natives = ["keccak256"] +pact412Natives = ["hash-keccak256"] initRefStore :: RefStore initRefStore = RefStore nativeDefs diff --git a/src/Pact/Native.hs b/src/Pact/Native.hs index 204afeccb..ed306ad80 100644 --- a/src/Pact/Native.hs +++ b/src/Pact/Native.hs @@ -117,7 +117,7 @@ import Pact.Types.Purity import Pact.Types.Runtime import Pact.Types.Version import Pact.Types.Namespace -import Crypto.Hash.Keccak256Native (keccak256) +import Crypto.Hash.Keccak256Native (Keccak256Error(..), keccak256) import Crypto.Hash.PoseidonNative (poseidon) import Crypto.Hash.HyperlaneMessageId (hyperlaneMessageId) @@ -1591,22 +1591,47 @@ poseidonHackAChainDef = defGasRNative keccak256Def :: NativeDef keccak256Def = defGasRNative - "keccak256" + "hash-keccak256" keccak256' - (funType tTyString [("chunks", TyList tTyString)]) + (funType tTyString [("bytes", TyList tTyString)]) [ - "(keccak256 [\"\"])" - , "(keccak256 [\"IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk/ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY+9BOzuztZI869P2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli+P5g2lv+m0JXGNptNr3nppdMTYikSj462PBK56fp4r/ej6eGaYgIkk80Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG/L84CrdVjE/ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w==\"])" - , "(keccak256 [\"IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaM=\", \"Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA=\", \"gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE=\", \"qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU=\", \"pCM4e4FRtBM+0j9tcYe1DsIgStkBrXTTluRCdODsr6o=\", \"4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08=\", \"2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA=\", \"cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj+YNpb/ptCVw=\", \"Y2m02veeml0xNiKRKPjrY8Ernp+niv96Pp4ZpiAiSTw=\", \"0Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDA=\", \"dOXaWHBAp2l11+OfRJSs5Uhvy/OAq3VYxP6JZWM1uC4=\", \"TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4=\", \"baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmw=\", \"4vKnl+SdwfE1MZgB+m85awb5deKhkKAj5HS2GOc=\"])" + "(hash-keccak256 [])" + , "(hash-keccak256 [\"\"])" + , "(hash-keccak256 [\"T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm-tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS-yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN_4Ir7DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylqnVqSpgt-tvzHvJVCDxLXGwbmaEH19Ov_9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji6L9Mdt4I4_xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ-u1Dyk7tCo3XYbx-292iiih8YM1Cr1-cdY5cclAjHAmlglY2ia_GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhDpYvZzyCxek1F4aoo7_VcAMYV71Mh_T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL_YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q\"])" + , "(hash-keccak256 [\"T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9g\", \"D5vralAgdYir887ujHfNilrUip4KoHTtOIc4NiSW0vs\", \"LIdUO7M0nqZJl84-e0JOqS0SL1fbsIVagDBYQ3_givs\", \"DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylo\", \"p1akqYLfrb8x7yVQg8S1xsG5mhB9fTr__biRR8LMTJo\", \"JkP0eOXi05Ouo3tMfLS16X2tzxa2tQquDztUns5HdG0\", \"ts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji4\", \"i_THbeCOP8RqoCH5icSTKcesrFpohVbXvLyypdS-adM\", \"KE6cQOxIOO6FkhIM4goLY17K2qhP1WkFCfVPd-NaQXw\", \"WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ8\", \"rtQ8pO7QqN12G8ftvdoooofGDNQq9fnHWOXHJQIxwJo\", \"WCVjaJr8ZeK3mnoraCAGZ3UukQF0bwMYTiOZ5O2INcs\", \"jprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhA\", \"6WL2c8gsXpNReGqKO_1XADGFe9TIf0_KMe1NUOFPIQc\", \"2gLLUFhwC3TqJBqLQdeEYWWPGyuQv9hKTCydZUOGGrM\", \"xWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q\"])" ] - "Compute the hash of a list of base64-encoded inputs. The hash is computed incrementally over all of the base64-decoded inputs." + "Compute the hash of a list of unpadded base64url-encoded inputs. The hash is computed incrementally over all of the decoded inputs." where keccak256' :: RNativeFun e keccak256' i = \case - [TList ls _ _] -> do - let numBytes = sum $ V.map (\s -> BS.length (T.encodeUtf8 (s ^. _TLitString))) ls - computeGas' i (GKeccak256 numBytes) - $ return $ toTerm $ keccak256 ls + args@[TList ls _ _] -> do + texts <- do + let mTexts = forM ls $ \case + TLiteral (LString s) _ -> Just s + _ -> Nothing + case mTexts of + Nothing -> argsError i args + Just x -> pure x + + forM_ texts $ \chunk -> do + -- At first I thought we should charge based on decoded bytes. + -- Both base64 and base64url have predictable byte-lengths from + -- both the input and output. However, charging on the encoded + -- length makes more sense, for two reasons: + -- + -- 1. We do not know if the decoding will fail here, and that + -- decoding is best left to the impl, not the nativedef + -- 2. Using the encoded bytes will potentially overcharge a bit, + -- but that's better than undercharging + let numBytesInChunk = BS.length (T.encodeUtf8 chunk) + computeGas' i (GKeccak256 numBytesInChunk) (pure ()) + + output <- case keccak256 texts of + Left (Keccak256Base64Exception msg) -> evalError' i ("Base64URL decode failed: " <> prettyString msg) + Left (Keccak256OpenSslException msg) -> evalError' i ("OpenSSL error when hashing: " <> prettyString msg) + Right output -> pure output + pure (toTerm output) + args -> argsError i args hyperlaneDefs :: NativeModule diff --git a/src/Pact/Types/Gas.hs b/src/Pact/Types/Gas.hs index c278b3a3c..bd8332f95 100644 --- a/src/Pact/Types/Gas.hs +++ b/src/Pact/Types/Gas.hs @@ -190,7 +190,7 @@ data GasArgs -- ^ Cost of hyperlane-decode-token-message on this size (in bytes) of the -- hyperlane TokenMessage base64-encoded string. | GKeccak256 !Int - -- ^ Cost of keccak256 per number of bytes + -- ^ Cost of keccak256 per number of bytes in a chunk. data IntOpThreshold = Pact43IntThreshold @@ -261,7 +261,7 @@ instance Pretty GasArgs where GPoseidonHashHackAChain len -> "GPoseidonHashHackAChain:" <> pretty len GHyperlaneMessageId len -> "GHyperlaneMessageId:" <> pretty len GHyperlaneDecodeTokenMessage len -> "GHyperlaneDecodeTokenMessage:" <> pretty len - GKeccak256 numBytes -> "GKeccak256:" <> pretty numBytes + GKeccak256 numBytesInChunk -> "GKeccak256:" <> pretty numBytesInChunk newtype GasLimit = GasLimit ParsedInteger deriving (Eq,Ord,Generic) diff --git a/tests/Keccak256Spec.hs b/tests/Keccak256Spec.hs index ec58353fa..bf2baf949 100644 --- a/tests/Keccak256Spec.hs +++ b/tests/Keccak256Spec.hs @@ -4,18 +4,15 @@ module Keccak256Spec (spec) where +import Control.Monad (forM_) import Crypto.Hash.Keccak256Native (keccak256) import Data.ByteString.Base16 qualified as Base16 -import Data.ByteString.Base64 qualified as Base64 -import Data.Default (def) import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding qualified as Text import Data.Vector qualified as V -import Pact.Types.Exp (Literal(..)) -import Pact.Types.Runtime (Term(..)) -import Test.Hspec -import Control.Monad (forM_) +import Pact.Types.Util (decodeBase64UrlUnpadded, encodeBase64UrlUnpadded) +import Test.Hspec (Spec, describe, it, shouldBe) data Reference = Reference { input :: Text @@ -57,15 +54,12 @@ hashChunks chunks = unwrap go go :: Either String Text go = do vals <- traverse (Base16.decode . Text.encodeUtf8) chunks - let lits = V.fromList $ flip map vals $ \lit -> - TLiteral - { _tLiteral = LString (Text.decodeUtf8 (Base64.encode lit)) - , _tInfo = def - } - rawHash <- Base64.decode - $ Text.encodeUtf8 - $ keccak256 lits - pure $ Text.decodeUtf8 $ Base16.encode rawHash + let texts = V.fromList $ map (Text.decodeUtf8 . encodeBase64UrlUnpadded) vals + case keccak256 texts of + Left err -> Left (show err) + Right out -> do + rawHash <- decodeBase64UrlUnpadded (Text.encodeUtf8 out) + pure (Text.decodeUtf8 (Base16.encode rawHash)) hashInputAndCompareToOutput :: Text -> Text -> IO () hashInputAndCompareToOutput i o = do diff --git a/tests/pact/keccak256.repl b/tests/pact/keccak256.repl index 3f8df3e2c..893bdd36f 100644 --- a/tests/pact/keccak256.repl +++ b/tests/pact/keccak256.repl @@ -1,27 +1,32 @@ (expect "computes the correct hash" - "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e/rYBF2FpHA=" - (keccak256 [""]) + "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e_rYBF2FpHA" + (hash-keccak256 []) ) (expect "computes the correct hash" - "DsjZ0g3fCnsCUelBpyYbVXUH/2KHtQQ2Ko8XNMWpEBI=" - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk/ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY+9BOzuztZI869P2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli+P5g2lv+m0JXGNptNr3nppdMTYikSj462PBK56fp4r/ej6eGaYgIkk80Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG/L84CrdVjE/ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w=="]) + "xdJGAYb3IzySfn2y3McDwOUAtlPKgic7e_rYBF2FpHA" + (hash-keccak256 [""]) +) + +(expect "computes the correct hash" + "DsjZ0g3fCnsCUelBpyYbVXUH_2KHtQQ2Ko8XNMWpEBI" + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaMTGHShyo9witFRnqlSweJJy1QNGWOSx56HdVQk_ufIkICMViciNZ7qUuihL7u5ad15YdK6UgN0k3VaX6BPDVChqibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTWkIzh7gVG0Ez7SP21xh7UOwiBK2QGtdNOW5EJ04OyvquF7O5CF4iJgs1ylOxXMUqu6dYr2eY-9BOzuztZI869P2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RBxvV4ZhOz0AvMG6nYvDyUoL1KW2Zdli-P5g2lv-m0JXGNptNr3nppdMTYikSj462PBK56fp4r_ej6eGaYgIkk80Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDB05dpYcECnaXXX459ElKzlSG_L84CrdVjE_ollYzW4Lk24ZZUJ6rRqGWExJuWUBCcy3UxBH0GqjN6sccD7QKlObaVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmzi8qeX5J3B8TUxmAH6bzlrBvl14qGQoCPkdLYY5w"]) ) (expect "computes the correct hash over chunked input" - "DsjZ0g3fCnsCUelBpyYbVXUH/2KHtQQ2Ko8XNMWpEBI=" - (keccak256 ["IP9FQ2ml0FuBp489sFgZ/qmwjCOE91ywq2qhFd1pDaM=", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA=", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE=", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU=", "pCM4e4FRtBM+0j9tcYe1DsIgStkBrXTTluRCdODsr6o=", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08=", "2z3tdVeppc+3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA=", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj+YNpb/ptCVw=", "Y2m02veeml0xNiKRKPjrY8Ernp+niv96Pp4ZpiAiSTw=", "0Tbe+7W7e6G5OPNn/S9j61ynbAsP8hueNsPwcjDPPDA=", "dOXaWHBAp2l11+OfRJSs5Uhvy/OAq3VYxP6JZWM1uC4=", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4=", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu+tYAtBDmw=", "4vKnl+SdwfE1MZgB+m85awb5deKhkKAj5HS2GOc="]) + "DsjZ0g3fCnsCUelBpyYbVXUH_2KHtQQ2Ko8XNMWpEBI" + (hash-keccak256 ["IP9FQ2ml0FuBp489sFgZ_qmwjCOE91ywq2qhFd1pDaM", "Exh0ocqPcIrRUZ6pUsHiSctUDRljkseeh3VUJP7nyJA", "gIxWJyI1nupS6KEvu7lp3Xlh0rpSA3STdVpfoE8NUKE", "qibJtEFIwNO5TRxKWaMayhWui9RKy3gz2OkcS4b6MTU", "pCM4e4FRtBM-0j9tcYe1DsIgStkBrXTTluRCdODsr6o", "4Xs7kIXiImCzXKU7FcxSq7p1ivZ5j70E7O7O1kjzr08", "2z3tdVeppc-3OCYSqKjz9FlH0aKc4pByko7Bk8ol1RA", "cb1eGYTs9ALzBup2Lw8lKC9SltmXZYvj-YNpb_ptCVw", "Y2m02veeml0xNiKRKPjrY8Ernp-niv96Pp4ZpiAiSTw", "0Tbe-7W7e6G5OPNn_S9j61ynbAsP8hueNsPwcjDPPDA", "dOXaWHBAp2l11-OfRJSs5Uhvy_OAq3VYxP6JZWM1uC4", "TbhllQnqtGoZYTEm5ZQEJzLdTEEfQaqM3qxxwPtAqU4", "baVYwF53thgoBvJtmv3z2gDGlBkiLIGGpu-tYAtBDmw", "4vKnl-SdwfE1MZgB-m85awb5deKhkKAj5HS2GOc"]) ) (expect "computes the correct hash" - "DqM+LjT1ckQGQCRMfx9fBGl86XE5vacqZVjYZjwCs4g=" - (keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm+tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS+yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN/4Ir7DItecXm5BEu/TYGnFjsxOeMIiLU2sPlX7/macWL0ylqnVqSpgt+tvzHvJVCDxLXGwbmaEH19Ov/9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo+N0fy+taji6L9Mdt4I4/xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge/qwA46QKV0LE5AlMKgw0cK91T8fnJ+u1Dyk7tCo3XYbx+292iiih8YM1Cr1+cdY5cclAjHAmlglY2ia/GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ/gvQvMYPekpf8/cMXtTenIxRmhDpYvZzyCxek1F4aoo7/VcAMYV71Mh/T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL/YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q=="]) + "DqM-LjT1ckQGQCRMfx9fBGl86XE5vacqZVjYZjwCs4g" + (hash-keccak256 ["T73FllCNJKKgAQ4UCYC4CfucbVXsdRJYkd2YXTdmW9gPm-tqUCB1iKvzzu6Md82KWtSKngqgdO04hzg2JJbS-yyHVDuzNJ6mSZfOPntCTqktEi9X27CFWoAwWEN_4Ir7DItecXm5BEu_TYGnFjsxOeMIiLU2sPlX7_macWL0ylqnVqSpgt-tvzHvJVCDxLXGwbmaEH19Ov_9uJFHwsxMmiZD9Hjl4tOTrqN7THy0tel9rc8WtrUKrg87VJ7OR3Rtts5vZ91EBs1OdVldUQPRP536eTcpJNMo-N0fy-taji6L9Mdt4I4_xGqgIfmJxJMpx6ysWmiFVte8vLKl1L5p0yhOnEDsSDjuhZISDOIKC2NeytqoT9VpBQn1T3fjWkF8WEZIvJg5uXTge_qwA46QKV0LE5AlMKgw0cK91T8fnJ-u1Dyk7tCo3XYbx-292iiih8YM1Cr1-cdY5cclAjHAmlglY2ia_GXit5p6K2ggBmd1LpEBdG8DGE4jmeTtiDXLjprpDilq8iCuI0JZ_gvQvMYPekpf8_cMXtTenIxRmhDpYvZzyCxek1F4aoo7_VcAMYV71Mh_T8ox7U1Q4U8hB9oCy1BYcAt06iQai0HXhGFljxsrkL_YSkwsnWVDhhqzxWRRdX3PubpgMzSI290C1gG0Gq4xfKdHTrbm3Q"]) ) (expect-failure "fails on non-base64 inputs" - (keccak256 ["foo"]) + (hash-keccak256 ["alibaba"]) ) (expect-failure "fails if any inputs are not base64-encoded" - (keccak256 ["Zm9v", "fooey"]) + (hash-keccak256 ["Zm9v", "fooey"]) )