-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of fee adjustment #165
Conversation
010e51c
to
4d4d18d
Compare
-- of outputs (incl. change) was bigger than the total input value which is | ||
-- by definition, impossible; unless we messed up real hard. | ||
collapse [] _ = | ||
invariant "outputs are bigger than inputs" (Coin 0) (const True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it should be const False
here, and let's replace Coin 0
with undefined
here ... this can never be reached.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed
-> UTxO | ||
-> m (Maybe ((TxIn, TxOut), UTxO)) | ||
pickUtxoRandom _ utxo = | ||
runMaybeT $ pickRandom utxo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this function / indirection is unnecessary here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
|
||
coinToDouble :: Coin -> Double | ||
coinToDouble = fromIntegral . getCoin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably simplify those above and not actually require Double
here. We do know the underlying type for coins (Word64
), so let's use that and arithmetic on integer (e.g. div
or quot
instead of /
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
-- the goal fixed by 'estimatedFee'. So, the actualFee just can't be lower | ||
-- than the goal. | ||
if ( actualFee < estimatedFee | ||
|| (coinToDouble actualFee) / 2.0 > (coinToDouble estimatedFee)) then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same remark here about Double
... We know the underlying type, so let's favor this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/Cardano/Wallet/Fee.hs
Outdated
-- Provides the API of Fee calculation. | ||
|
||
|
||
module Cardano.Wallet.Fee |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for an extra module here, let's have that in Cardano.Wallet.CoinSelection
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relocated FeeOptions
and FeeErrors
to Cardano.Wallet.CoinSelection
removeDust = L.filter (> (dustThreshold opt)) | ||
|
||
reduceChangeOutputs | ||
:: ([Coin] -> [Coin]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the abstraction here, we can use removeDust
directly from above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
remFee <- lift $ runMaybeT $ coverRemainingFee remainingFee utxo | ||
case remFee of | ||
Nothing -> | ||
throwE CannotCoverFee |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can have the error a bit more explicit here 🤔 ? Saying that we cannot cover for fee because there's not enough inputs? And actually communicate about the missing amount?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added missing amount
arg
map (\a -> (feeForOut a, a)) outs | ||
where | ||
totalOut :: Coin | ||
totalOut = (Coin . sum . map getCoin) outs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it could overflow 🤔 ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
outputs can add to more than upperBound of Coin. I used Word64
here, and checking in feeForOut
just below overflowing
src/Cardano/Wallet/Fee.hs
Outdated
import GHC.Generics | ||
( Generic ) | ||
|
||
newtype Fee = Fee { getFee :: Coin } deriving (Show, Eq) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need the newtype here. Fee are just Coin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now no more Fee
just Coin
ecd5b66
to
fea97a0
Compare
totalOuts :: Word64 | ||
totalOuts = (sum . map getCoin) outs | ||
|
||
feeForOut :: Coin -> Coin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for the simplicity and error plucking I am backed with double
, fExtraUtxo = [1] | ||
, fFee = 5 | ||
, fDust = 0 | ||
}) (Left $ CannotCoverFee 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe should be CannotCoverFee 1
as fChngs
and fExtraUtxo
gives 4 ... now because of MaybeT usage signalling about missing coins before random picking started
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will be corrected in coming version
, fExtraUtxo = [2] | ||
, fFee = 5 | ||
, fDust = 0 | ||
}) (Left $ OutOfBoundFee 0 5) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one I do not understand. From senderPaysFee
I have :
coinSel' = CoinSelection {inputs = [(TxIn {inputId = Hash {getHash = "\170\223\131K\130@\DLE2\202\159"}, inputIx = 0},TxOut {address = Address {getAddress = "ADDR03"}, coin = Coin {getCoin = 10}}),(TxIn {inputId = Hash {getHash = "\219\CANf\153\159\&9\189\154\&9\133"}, inputIx = 0},TxOut {address = Address {getAddress = "ADDR01"}, coin = Coin {getCoin = 2}})], outputs = [TxOut {address = Address {getAddress = "ADDR01"}, coin = Coin {getCoin = 7}}], change = [Coin {getCoin = 5}]}
and after computeFee coinSel'
I have Coin 0 ... The computation is :
collapse : p>m : Coin {getCoin = 10} > Coin {getCoin = 7} ps:[Coin {getCoin = 2}] ms[Coin {getCoin = 5}]
collapse : p<m : Coin {getCoin = 3} < Coin {getCoin = 5} ps:[Coin {getCoin = 2}] ms[]
collapse : p==m : Coin {getCoin = 2} ps:[] ms[]
collapse : plus : []
I need to think about it - though it should be Right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in new commit
added logs which I use in the comment to illustrate the point I do not understand yet. Will remove them before the last commit |
cardano-wallet.cabal
Outdated
@@ -69,6 +69,7 @@ library | |||
Cardano.Wallet.Binary | |||
Cardano.Wallet.Binary.Packfile | |||
Cardano.Wallet.CoinSelection | |||
Cardano.Wallet.CoinSelection.Fee |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's completely remove this module and have it merged with Cardano.Wallet.CoinSelection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -11,6 +11,8 @@ | |||
|
|||
module Cardano.Wallet.CoinSelection.Random | |||
( random | |||
, pickRandom | |||
, distance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe distance is something we want to have in Primitive.Types
🤔 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
computeFee | ||
:: CoinSelection | ||
-> Coin | ||
computeFee (CoinSelection inps outs chgs) = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this whole algorithm here exists only because in the original implementation, we were in the generic realm, where there was no concrete instance for Coin
. Here, we know that Coin = Coin Word64
with maxBound :: Coin
being much much less than maxBound :: Word64
; We could in practice makes the computation Word64
(or Natural
) if we really really want the safety here, and at the end, convert it back to a Coin.
Also, we can pretty much safely assume (i.e. enforce with an invariant) that the resulting fee Coin is less than the max bound for Coin (otherwise that's a very very expensive transaction we have there!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for this comment
fea97a0
to
cde75b8
Compare
added almost 25 unit tests and made sure they pass. Reshuffled the code as suggested by reviewer. Rebased. Next commit : properties tests |
6b85a3b
to
f06d2fa
Compare
1acdbdb
to
c87b71d
Compare
c87b71d
to
40a761b
Compare
Remove pickUtxo argument and clean the code
Finetuning tests and adding rest properties Add estimate function pointers Improving on properties
40a761b
to
356d7f8
Compare
There is a failing properties:
which doesn't fail all the time but only when small enough coins are generated. Seems like some dust remains in the final selection 🤔 ... I haven't much looked into that but, changing the coin arbitrary generator to NOTE: I've removed / merged together some of the other properties to keep it simple and, only about properties that are rather useful to verify. |
as it is now I have not encountered failure when running:
Moreover, I see in
which means dust threshold is Nevertheless, I confirm that when coin is between (1,10) the test fails I see that the below is expected :
but after
meaning because of small coins Note. Of course, when Coin is (1,10) we cannot cover fees that are (100000, 500000) so third property will find satisfying case. As expected :
|
I am not sure about this last commit @paweljakubas: why do we even have 0 coins as part of the selection 🤔 ? |
well I believe, |
Indeed. I've moved the fix into the largest first implementation instead as it makes more sense to have it here and return valid coin selections in a first place! |
4b1d41f
to
215f429
Compare
Issue Number
#92
Overview
Comments