web3-0.8.0.0: unit/Data/Solidity/Test/EncodingSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
module Data.Solidity.Test.EncodingSpec where
import Control.Exception (evaluate)
import Data.Monoid ((<>))
import Data.Text (Text)
import Data.Tuple.OneTuple (OneTuple (..))
import Generics.SOP (Generic, Rep)
import Test.Hspec
import Data.Solidity.Abi (AbiGet, AbiPut, GenericAbiGet,
GenericAbiPut)
import Data.Solidity.Abi.Codec (decode, decode', encode, encode')
import Data.Solidity.Prim (Address, Bytes, BytesN, IntN,
ListN, UIntN)
import Data.Solidity.Prim.Address (fromHexString, toHexString)
spec :: Spec
spec = do
intNTest
bytesTest
bytesNTest
vectorTest
dynamicArraysTest
tuplesTest
addressTest
intNTest :: Spec
intNTest =
describe "uint tests" $ do
it "can encode int16" $
let decoded = (-1) :: IntN 16
encoded = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
in roundTrip decoded encoded
it "can encode larger uint256" $
let decoded = (2 ^ 255) - 1 :: UIntN 256
encoded = "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
in roundTrip decoded encoded
bytesTest :: Spec
bytesTest = do
describe "bytes tests" $ do
it "can encode empty bytes" $ do
let decoded :: Bytes
decoded = "0x"
encoded = "0x0000000000000000000000000000000000000000000000000000000000000000"
in roundTrip decoded encoded
it "can encode short bytes" $ do
let decoded :: Bytes
decoded = "0xc3a40000c3a4"
encoded = "0x0000000000000000000000000000000000000000000000000000000000000006"
<> "0xc3a40000c3a40000000000000000000000000000000000000000000000000000"
in roundTrip decoded encoded
it "can encode long bytes" $ do
let decoded :: Bytes
decoded = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1"
encoded = "0x000000000000000000000000000000000000000000000000000000000000009f"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff100"
in roundTrip decoded encoded
bytesNTest :: Spec
bytesNTest =
describe "sized bytes tests" $ do
it "can encode Bytes1" $ do
let decoded = "0xcf" :: BytesN 1
encoded = "0xcf00000000000000000000000000000000000000000000000000000000000000"
roundTrip decoded encoded
it "can encode Bytes12" $ do
let decoded = "0x6761766f66796f726b000000" :: BytesN 12
encoded = "0x6761766f66796f726b0000000000000000000000000000000000000000000000"
roundTrip decoded encoded
it "can pad shorter hex-literals" $ do
let literal = "0xc3a4" :: BytesN 4
expected = "0xc3a40000" :: BytesN 4
literal `shouldBe` expected
it "can pad shorter string-literals" $ do
let literal = "hello" :: BytesN 8
expected = "0x68656c6c6f000000" :: BytesN 8
literal `shouldBe` expected
it "fails on too long literals" $ do
let literal = "hello" :: BytesN 4
evaluate literal `shouldThrow` errorCall "Invalid Size"
vectorTest :: Spec
vectorTest =
describe "statically sized array tests" $ do
it "can encode statically sized vectors of addresses" $ do
let decoded = [False, True] :: ListN 2 Bool
encoded = "0x0000000000000000000000000000000000000000000000000000000000000000"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
in roundTrip decoded encoded
it "can encode statically sized vectors of statically sized bytes"$ do
let elem1 = "0xcf"
elem2 = "0x68"
elem3 = "0x4d"
elem4 = "0xfb"
decoded = [elem1, elem2, elem3, elem4] :: ListN 4 (BytesN 1)
encoded = "0xcf00000000000000000000000000000000000000000000000000000000000000"
<> "0x6800000000000000000000000000000000000000000000000000000000000000"
<> "0x4d00000000000000000000000000000000000000000000000000000000000000"
<> "0xfb00000000000000000000000000000000000000000000000000000000000000"
in roundTrip decoded encoded
dynamicArraysTest :: Spec
dynamicArraysTest = do
describe "dynamically sized array tests" $ do
it "can encode dynamically sized lists of bools" $ do
let decoded = [True, True, False] :: [Bool]
encoded = "0x0000000000000000000000000000000000000000000000000000000000000003"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000000"
in roundTrip decoded encoded
tuplesTest :: Spec
tuplesTest =
describe "tuples test" $ do
it "can encode 2-tuples with both static args" $ do
let decoded = (True, False)
encoded = "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000000"
in roundTripGeneric decoded encoded
it "can encode 1-tuples with dynamic arg" $ do
let decoded = OneTuple ([True, False] :: [Bool])
encoded = "0x0000000000000000000000000000000000000000000000000000000000000020"
<> "0x0000000000000000000000000000000000000000000000000000000000000002"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000000"
in roundTripGeneric decoded encoded
it "can encode 4-tuples with a mix of args - (UInt32, String, Boolean, Array Int256)" $ do
let decoded = (1 :: IntN 32, "dave" :: Text, True, [1, 2, 3] :: [IntN 256])
encoded = "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000080"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x00000000000000000000000000000000000000000000000000000000000000c0"
<> "0x0000000000000000000000000000000000000000000000000000000000000004"
<> "0x6461766500000000000000000000000000000000000000000000000000000000"
<> "0x0000000000000000000000000000000000000000000000000000000000000003"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000002"
<> "0x0000000000000000000000000000000000000000000000000000000000000003"
in roundTripGeneric decoded encoded
it "can do something really complicated" $ do
let uint = 1 :: UIntN 256
int = (-1) :: IntN 256
bool = True
int224 = 221 :: IntN 224
bools = [True, False] :: ListN 2 Bool
ints = [1, -1, 3] :: [IntN 32]
string = "hello" :: Text
bytes16 = "0x12345678123456781234567812345678" :: BytesN 16
elem1 = "0x1234" :: BytesN 2
bytes2s = [ [elem1, elem1, elem1, elem1]
, [elem1, elem1, elem1, elem1]
] :: [ListN 4 (BytesN 2)]
decoded = (uint, int, bool, int224, bools, ints, string, bytes16, bytes2s)
encoded = "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x00000000000000000000000000000000000000000000000000000000000000dd"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0x0000000000000000000000000000000000000000000000000000000000000000"
<> "0x0000000000000000000000000000000000000000000000000000000000000140"
<> "0x00000000000000000000000000000000000000000000000000000000000001c0"
<> "0x1234567812345678123456781234567800000000000000000000000000000000"
<> "0x0000000000000000000000000000000000000000000000000000000000000200"
<> "0x0000000000000000000000000000000000000000000000000000000000000003"
<> "0x0000000000000000000000000000000000000000000000000000000000000001"
<> "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
<> "0x0000000000000000000000000000000000000000000000000000000000000003"
<> "0x0000000000000000000000000000000000000000000000000000000000000005"
<> "0x68656c6c6f000000000000000000000000000000000000000000000000000000"
<> "0x0000000000000000000000000000000000000000000000000000000000000002"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
<> "0x1234000000000000000000000000000000000000000000000000000000000000"
in roundTripGeneric decoded encoded
addressTest :: Spec
addressTest =
describe "address test" $ do
it "can abi encode address" $ do
let decoded = "0x4af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a" :: Address
encoded = "0x0000000000000000000000004af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a"
in roundTrip decoded encoded
it "can decode address from/to bytestring" $ do
fromHexString "0x4af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a"
`shouldBe` Right ("0x4af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a" :: Address)
toHexString "0x4af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a"
`shouldBe` "0x4af013afbadb22d8a88c92d68fc96b033b9ebb8a"
it "fails for invalid address length" $ do
evaluate (fromHexString "0x0")
`shouldThrow` errorCall "base16: input: invalid length"
fromHexString "0x4af013AfBAdb22D8A88c92D68Fc96B033b9Ebb8a00"
`shouldBe` Left "Incorrect address length: 21"
-- | Run encoded/decoded comaration
roundTrip :: ( Show a
, Eq a
, AbiPut a
, AbiGet a
)
=> a
-> Bytes
-> IO ()
roundTrip decoded encoded = do
encoded `shouldBe` encode decoded
decode encoded `shouldBe` Right decoded
-- | Run generic encoded/decoded comaration
roundTripGeneric :: ( Show a
, Eq a
, Generic a
, Rep a ~ rep
, GenericAbiPut rep
, GenericAbiGet rep
)
=> a
-> Bytes
-> IO ()
roundTripGeneric decoded encoded = do
encoded `shouldBe` encode' decoded
decode' encoded `shouldBe` Right decoded