hevm-0.41.0: src/EVM/Transaction.hs
module EVM.Transaction where
import Prelude hiding (Word)
import EVM.Concrete
import EVM.FeeSchedule
import EVM.Keccak (keccak)
import EVM.Precompiled (execute)
import EVM.RLP
import EVM.Types
import Data.Aeson (FromJSON (..))
import Data.ByteString (ByteString)
import Data.Maybe (isNothing)
import qualified Data.Aeson as JSON
import qualified Data.Aeson.Types as JSON
import qualified Data.ByteString as BS
data Transaction = Transaction
{ txData :: ByteString,
txGasLimit :: W256,
txGasPrice :: W256,
txNonce :: W256,
txR :: W256,
txS :: W256,
txToAddr :: Maybe Addr,
txV :: W256,
txValue :: W256
} deriving Show
ecrec :: W256 -> W256 -> W256 -> W256 -> Maybe Addr
ecrec e v r s = (num . word) <$> EVM.Precompiled.execute 1 input 32
where input = BS.concat (word256Bytes <$> [e, v, r, s])
sender :: Int -> Transaction -> Maybe Addr
sender chainId tx = ecrec hash v' (txR tx) (txS tx)
where hash = keccak $ signingData chainId tx
v = txV tx
v' = if v == 27 || v == 28 then v
else 28 - mod v 2
signingData :: Int -> Transaction -> ByteString
signingData chainId tx =
if v == (chainId * 2 + 35) || v == (chainId * 2 + 36)
then eip155Data
else normalData
where v = fromIntegral (txV tx)
to' = case txToAddr tx of
Just a -> BS $ word160Bytes a
Nothing -> BS mempty
normalData = rlpList [rlpWord256 (txNonce tx),
rlpWord256 (txGasPrice tx),
rlpWord256 (txGasLimit tx),
to',
rlpWord256 (txValue tx),
BS (txData tx)]
eip155Data = rlpList [rlpWord256 (txNonce tx),
rlpWord256 (txGasPrice tx),
rlpWord256 (txGasLimit tx),
to',
rlpWord256 (txValue tx),
BS (txData tx),
rlpWord256 (fromIntegral chainId),
rlpWord256 0x0,
rlpWord256 0x0]
txGasCost :: FeeSchedule Word -> Transaction -> Word
txGasCost fs tx =
let calldata = txData tx
zeroBytes = BS.count 0 calldata
nonZeroBytes = BS.length calldata - zeroBytes
baseCost = g_transaction fs
+ if isNothing (txToAddr tx) then g_txcreate fs else 0
zeroCost = g_txdatazero fs
nonZeroCost = g_txdatanonzero fs
in baseCost + zeroCost * (fromIntegral zeroBytes) + nonZeroCost * (fromIntegral nonZeroBytes)
instance FromJSON Transaction where
parseJSON (JSON.Object val) = do
tdata <- dataField val "data"
gasLimit <- wordField val "gasLimit"
gasPrice <- wordField val "gasPrice"
nonce <- wordField val "nonce"
r <- wordField val "r"
s <- wordField val "s"
toAddr <- addrFieldMaybe val "to"
v <- wordField val "v"
value <- wordField val "value"
return $ Transaction tdata gasLimit gasPrice nonce r s toAddr v value
parseJSON invalid =
JSON.typeMismatch "Transaction" invalid