Skip to content
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

Ether delayed gas check #301

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 10 additions & 22 deletions src/GWallet.Backend/Account.fs
Original file line number Diff line number Diff line change
Expand Up @@ -248,24 +248,10 @@ module Account =
txId
amountTransferredPlusFeeIfCurrencyFeeMatches
fee.FeeValue

// FIXME: if out of gas, miner fee is still spent, we should inspect GasUsed and use it for the call to
// SaveOutgoingTransactionInCache
let private CheckIfOutOfGas (transactionMetadata: IBlockchainFeeInfo) (txHash: string)
: Async<unit> =
async {
match transactionMetadata with
| :? Ether.TransactionMetadata as etherTxMetadata ->
try
let! outOfGas = Ether.Server.IsOutOfGas transactionMetadata.Currency txHash etherTxMetadata.Fee.GasLimit
if outOfGas then
return failwith <| SPrintF1 "Transaction ran out of gas: %s" txHash
with
| ex ->
return raise <| Exception(SPrintF1 "An issue occurred while trying to check if the following transaction ran out of gas: %s" txHash, ex)
| _ ->
()
}
match fee with
| :? Ether.TransactionMetadata as etherTxMetadata ->
Caching.Instance.StoreUnconfirmedTransaction fee.Currency txId etherTxMetadata.Fee.GasLimit
| _ -> ()

// FIXME: broadcasting shouldn't just get N consistent replies from FaultTolerantClient,
// but send it to as many as possible, otherwise it could happen that some server doesn't
Expand All @@ -274,6 +260,9 @@ module Account =
async {
let currency = trans.TransactionInfo.Proposal.Amount.Currency

if currency.IsEtherBased() then
do! Ether.Server.CheckIfAddressIsAValidPaymentDestination Currency.ETH trans.TransactionInfo.Proposal.DestinationAddress

let! txId =
if currency.IsEtherBased() then
Ether.Account.BroadcastTransaction trans ignoreHigherMinerFeeThanAmount
Expand All @@ -282,8 +271,6 @@ module Account =
else
failwith <| SPrintF1 "Unknown currency %A" currency

do! CheckIfOutOfGas trans.TransactionInfo.Metadata txId

SaveOutgoingTransactionInCache trans.TransactionInfo.Proposal trans.TransactionInfo.Metadata txId

let uri = BlockExplorer.GetTransaction currency txId
Expand Down Expand Up @@ -441,6 +428,9 @@ module Account =
async {
do! ValidateAddress currency destination

if currency.IsEtherBased() then
do! Ether.Server.CheckIfAddressIsAValidPaymentDestination Currency.ETH destination

let! txId =
match txMetadata with
| :? UtxoCoin.TransactionMetadata as btcTxMetadata ->
Expand All @@ -460,8 +450,6 @@ module Account =
| _ ->
failwith "Unknown tx metadata type"

do! CheckIfOutOfGas txMetadata txId

let transactionProposal =
{
OriginMainAddress = baseAccount.PublicAddress
Expand Down
35 changes: 34 additions & 1 deletion src/GWallet.Backend/Caching.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type CachedNetworkData =
UsdPrice: Map<Currency,CachedValue<decimal>>;
Balances: Map<Currency,Map<PublicAddress,CachedValue<decimal>>>;
OutgoingTransactions: Map<Currency,Map<PublicAddress,Map<string,CachedValue<decimal>>>>;
UnconfirmedTransactions: Map<Currency,list<string * int64>>
}
member self.GetLeastOldDate() =
let allDates =
Expand All @@ -35,6 +36,7 @@ type CachedNetworkData =
UsdPrice = Map.empty
Balances = Map.empty
OutgoingTransactions = Map.empty
UnconfirmedTransactions = Map.empty
}

static member FromDietCache (dietCache: DietCache): CachedNetworkData =
Expand All @@ -52,7 +54,8 @@ type CachedNetworkData =
yield (Currency.Parse currencyStr),Map.empty.Add(address,(balance,now))
} |> Map.ofSeq
{ UsdPrice = fiatPrices; Balances = balances
OutgoingTransactions = Map.empty; }
OutgoingTransactions = Map.empty;
UnconfirmedTransactions = Map.empty }

member self.ToDietCache(readOnlyAccounts: seq<ReadOnlyAccount>) =
let rec extractAddressesFromAccounts (acc: Map<PublicAddress,List<DietCurrency>>) (accounts: List<IAccount>)
Expand Down Expand Up @@ -647,4 +650,34 @@ module Caching =
member __.FirstRun
with get() = firstRun

member self.StoreUnconfirmedTransaction (currency: Currency) (txHash: string) (gasLimit: int64) =
lock cacheFiles.CachedNetworkData (fun _ ->
let newTransactions =
match sessionCachedNetworkData.UnconfirmedTransactions |> Map.tryFind currency with
| Some transactionsForCurrency -> (txHash, gasLimit) :: transactionsForCurrency
| None -> List.singleton (txHash, gasLimit)
let newCachedData =
{ sessionCachedNetworkData with
UnconfirmedTransactions =
sessionCachedNetworkData.UnconfirmedTransactions
|> Map.add currency newTransactions }
SaveNetworkDataToDisk newCachedData
)

member self.RemoveUnconfirmedTransaction (currency: Currency) (txHash: string) =
lock cacheFiles.CachedNetworkData (fun _ ->
match sessionCachedNetworkData.UnconfirmedTransactions |> Map.tryFind currency with
| Some transactionsForCurrency ->
let newTransactionForCurrency =
transactionsForCurrency
|> List.filter (fun (hash, _) -> hash <> txHash)
let newCachedData =
{ sessionCachedNetworkData with
UnconfirmedTransactions =
sessionCachedNetworkData.UnconfirmedTransactions
|> Map.add currency newTransactionForCurrency }
SaveNetworkDataToDisk newCachedData
| None -> ()
)

let Instance = MainCache (None, TimeSpan.FromDays 1.0)
19 changes: 19 additions & 0 deletions src/GWallet.Frontend.Console/UserInteraction.fs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,24 @@ module UserInteraction =
// All balances are fetched and their currency names printed, put a dot at the end of "Retrieving balances... " line.
Console.Write '.'

let unconfirmedStatuses = ResizeArray<string>()
for account in accounts do
if account.Currency.IsEtherBased() then
let cache = Caching.Instance.GetLastCachedData()
match cache.UnconfirmedTransactions |> Map.tryFind account.Currency with
| Some unconfirmedTransactions ->
for txHash, gasSpent in unconfirmedTransactions do
try
let! isOutOfGas = Ether.Server.IsOutOfGas account.Currency txHash gasSpent
if isOutOfGas then
unconfirmedStatuses.Add(sprintf "Transaction ran out of gas: 0x%s" txHash)
else
Caching.Instance.RemoveUnconfirmedTransaction account.Currency txHash
with
| :? TimeoutException ->
unconfirmedStatuses.Add(sprintf "Timed out when checking transaction 0x%s" txHash)
| None -> ()

let maybeTotalInUsd, totals = displayTotalAndSumFiatBalance currencyTotals
return
seq {
Expand All @@ -362,6 +380,7 @@ module UserInteraction =
seq {
yield! statuses
yield! totals
yield! unconfirmedStatuses
yield String.Empty // this ends up being simply an Environment.NewLine
yield sprintf "Total estimated value in USD: %s"
(Formatting.DecimalAmountRounding CurrencyType.Fiat totalInUsd)
Expand Down
Loading