packages feed

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