Skip to content

Commit

Permalink
txsize now just counts the serialized bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
Jared Corduan committed Jul 10, 2020
1 parent 6611345 commit 2724f7c
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 64 deletions.
Expand Up @@ -66,7 +66,9 @@ module Shelley.Spec.Ledger.LedgerState
nullWitHashes,
diffWitHashes,
minfee,
minfeeBound,
txsize,
txsizeBound,
produced,
consumed,
verifiedWits,
Expand Down Expand Up @@ -705,8 +707,13 @@ genesisState genDelegs0 utxo0 =
dState = emptyDState {_genDelegs = GenDelegs genDelegs0}

-- | Implementation of abstract transaction size
txsize :: forall crypto. (Crypto crypto) => Tx crypto -> Integer
txsize tx = numInputs * inputSize + numOutputs * outputSize + rest
txsize :: Tx crypto -> Integer
txsize = fromIntegral . BSL.length . txFullBytes

-- | Convenience Function to bound the txsize function.
-- | It can be helpful for coin selection.
txsizeBound :: forall crypto. (Crypto crypto) => Tx crypto -> Integer
txsizeBound tx = numInputs * inputSize + numOutputs * outputSize + rest
where
uint = 5
smallArray = 1
Expand All @@ -723,9 +730,13 @@ txsize tx = numInputs * inputSize + numOutputs * outputSize + rest
rest = fromIntegral $ BSL.length (txFullBytes tx) - extraSize txbody

-- | Minimum fee calculation
minfee :: forall crypto. (Crypto crypto) => PParams -> Tx crypto -> Coin
minfee :: PParams -> Tx crypto -> Coin
minfee pp tx = Coin $ fromIntegral (_minfeeA pp) * txsize tx + fromIntegral (_minfeeB pp)

-- | Minimum fee bound using txsizeBound
minfeeBound :: forall crypto. (Crypto crypto) => PParams -> Tx crypto -> Coin
minfeeBound pp tx = Coin $ fromIntegral (_minfeeA pp) * txsizeBound tx + fromIntegral (_minfeeB pp)

-- | Compute the lovelace which are created by the transaction
produced ::
(Crypto crypto) =>
Expand Down
Expand Up @@ -499,16 +499,16 @@ sizeTests :: TestTree
sizeTests =
testGroup
"Fee Tests"
[ testCase "simple utxo" $ sizeTest p txSimpleUTxOBytes16 txSimpleUTxO 143,
testCase "multiple utxo" $ sizeTest p txMutiUTxOBytes16 txMutiUTxO 470,
testCase "register stake key" $ sizeTest p txRegisterStakeBytes16 txRegisterStake 158,
testCase "delegate stake key" $ sizeTest p txDelegateStakeBytes16 txDelegateStake 194,
testCase "deregister stake key" $ sizeTest p txDeregisterStakeBytes16 txDeregisterStake 158,
testCase "register stake pool" $ sizeTest p txRegisterPoolBytes16 txRegisterPool 220,
testCase "retire stake pool" $ sizeTest p txRetirePoolBytes16 txRetirePool 157,
testCase "metadata" $ sizeTest p txWithMDBytes16 txWithMD 162,
testCase "multisig" $ sizeTest p txWithMultiSigBytes16 (txWithMultiSig p) 209,
testCase "reward withdrawal" $ sizeTest p txWithWithdrawalBytes16 txWithWithdrawal 184
[ testCase "simple utxo" $ sizeTest p txSimpleUTxOBytes16 txSimpleUTxO 73,
testCase "multiple utxo" $ sizeTest p txMutiUTxOBytes16 txMutiUTxO 194,
testCase "register stake key" $ sizeTest p txRegisterStakeBytes16 txRegisterStake 88,
testCase "delegate stake key" $ sizeTest p txDelegateStakeBytes16 txDelegateStake 124,
testCase "deregister stake key" $ sizeTest p txDeregisterStakeBytes16 txDeregisterStake 88,
testCase "register stake pool" $ sizeTest p txRegisterPoolBytes16 txRegisterPool 150,
testCase "retire stake pool" $ sizeTest p txRetirePoolBytes16 txRetirePool 87,
testCase "metadata" $ sizeTest p txWithMDBytes16 txWithMD 92,
testCase "multisig" $ sizeTest p txWithMultiSigBytes16 (txWithMultiSig p) 139,
testCase "reward withdrawal" $ sizeTest p txWithWithdrawalBytes16 txWithWithdrawal 114
]
where
p :: Proxy ShortHash
Expand Down
Expand Up @@ -38,7 +38,7 @@ import Shelley.Spec.Ledger.Credential
)
import Shelley.Spec.Ledger.Keys (Hash, KeyRole (..), asWitness)
import Shelley.Spec.Ledger.LedgerState
( minfee,
( minfeeBound,
_dstate,
_ptrs,
_rewards,
Expand Down Expand Up @@ -222,7 +222,7 @@ genTx
let metadata = SNothing -- TODO generate metadata

-- calculate real fees of witnesses transaction
let minimalFees = minfee pparams (Tx txBody wits metadata)
let minimalFees = minfeeBound pparams (Tx txBody wits metadata)

-- discard generated transaction if the balance cannot cover the fees
if minimalFees > balance_
Expand Down
Expand Up @@ -53,7 +53,7 @@ import Shelley.Spec.Ledger.Delegation.Certificates
isRetirePool,
isTreasuryMIRCert,
)
import Shelley.Spec.Ledger.LedgerState (txsize)
import Shelley.Spec.Ledger.LedgerState (txsizeBound)
import Shelley.Spec.Ledger.PParams
( PParamsUpdate,
pattern ProposedPPUpdates,
Expand Down Expand Up @@ -332,7 +332,7 @@ propAbstractSizeBoundsBytes = property $ do
forAllTraceFromInitState @(LEDGER ShortHash) testGlobals tl (genEnv p) genesisLedgerState $ \tr -> do
let txs :: [Tx ShortHash]
txs = traceSignals OldestFirst tr
all (\tx -> txsize tx >= numBytes tx) txs
all (\tx -> txsizeBound tx >= numBytes tx) txs
where
p :: Proxy ShortHash
p = Proxy
Expand All @@ -349,7 +349,7 @@ propAbstractSizeNotTooBig = property $ do
-- an acceptableMagnitude of three, though.
acceptableMagnitude = (3 :: Integer)
numBytes = toInteger . BS.length . serialize'
notTooBig txb = txsize txb <= acceptableMagnitude * numBytes txb
notTooBig txb = txsizeBound txb <= acceptableMagnitude * numBytes txb
forAllTraceFromInitState @(LEDGER ShortHash) testGlobals tl (genEnv p) genesisLedgerState $ \tr -> do
let txs :: [Tx ShortHash]
txs = traceSignals OldestFirst tr
Expand Down
Expand Up @@ -515,7 +515,7 @@ testEmptyInputSet =
testFeeTooSmall :: Assertion
testFeeTooSmall =
testInvalidTx
[UtxowFailure (UtxoFailure (FeeTooSmallUTxO (Coin 210) (Coin 1)))]
[UtxowFailure (UtxoFailure (FeeTooSmallUTxO (Coin 98) (Coin 1)))]
$ aliceGivesBobLovelace
AliceToBob
{ input = (TxIn genesisId 0),
Expand Down
48 changes: 3 additions & 45 deletions shelley/chain-and-ledger/formal-spec/txsize.tex
Expand Up @@ -2,48 +2,6 @@ \section{Implementation of \fun{txSize}}
\label{sec:txSize}

The minimum fee calculation in Figure~\ref{fig:defs:protocol-parameters-helpers}
depends on an abstract $\fun{txSize}$ function, which we describe here.

We define $\fun{txSize}$ as:
$$\fun{txSize}~tx~=~\var{numInputs} \cdot 40 + \var{numOutputs} \cdot 65 + \var{rest},$$
where
\begin{itemize}
\item $\var{numInputs}$ is the number of transaction inputs in $\var{tx}$,
\item $\var{numOutputs}$ is the number of transaction outputs in $\var{tx}$,
\item $\var{tx'}$ is identical to $\var{tx}$, except that it has
\begin{itemize}
\item no inputs,
\item no outputs,
\item a fee of zero
\end{itemize}
\item $\var{rest}$ is the number of serialized bytes in $\var{tx'}$,
as defined in Appendix~\ref{sec:cddl},
\end{itemize}


We now justify this calculation.
Using the number of bytes in the serialized transaction is problematic for a couple of reasons.
First, the fee is listed in the transaction, so there is a circularity problem.
Second, algorithms implementing coin selection
(choosing which unspent transaction outputs to consume)
would have to make heavy use of serialization.
Besides these two issues, however, the number of serialized bytes
does exactly what we want.

Therefore we calculate the transaction size by first computing
the number of bytes in a modifed version of the transaction
that has no inputs, no outputs, and has a fee of zero,
and then we adjust accordingly by the number of inputs and outputs.

As given by the CDDL spec in Appendix~\ref{sec:cddl},
a transaction input is serialized as:
\begin{lstlisting}[backgroundcolor = \color{lightgray}]
transaction_input = [ transaction_id : $hash32 , index : uint ]
\end{lstlisting}
which is bounded by 40 bytes.

Similarly, a transaction output is serialized as:
\begin{lstlisting}[backgroundcolor = \color{lightgray}]
transaction_output = [address, amount : uint]
\end{lstlisting}
which is bounded by 65 bytes.
depends on an abstract $\fun{txSize}$ function.
We have implemented $\fun{txSize}$ as the number of bytes in the CBOR serialization
of the transaction, as defined in Appendix~\ref{sec:cddl}.

0 comments on commit 2724f7c

Please sign in to comment.