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

PE-3173: Utilize Turbo Uploads for Quick Sync #1362

Merged
merged 31 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d36e8b6
feat(payment method selector): factors out a component in order to ma…
matibat Sep 12, 2023
c944ac1
feat(snapshots): let snapshots be uploaded with Turbo PE-3173
matibat Sep 12, 2023
96c167f
feat(snapshots): missing await PE-3173
matibat Sep 12, 2023
ae2937d
Merge remote-tracking branch 'origin/dev' into PE-3173
matibat Sep 13, 2023
2c461a9
feat(create snapshot cubit): free turbo uploads PE-3173
matibat Sep 14, 2023
598c77e
feat(upload form): adds back the call to _getInsufficientBalanceMessa…
matibat Sep 14, 2023
546900d
test(create snapshot): fixes nbronken tests PE-3173
matibat Sep 14, 2023
ea9f1c7
chore(create snapshot cubit & upload plan): cleanup PE-3173
matibat Sep 22, 2023
6636abd
feat(new button): makes the option for snaqpshots be enabled even whe…
matibat Sep 25, 2023
90f4685
Merge pull request #1382 from ardriveapp/PE-4665
matibat Sep 26, 2023
91b078b
feat(snapshot & upload): makes no balance message be part of payment …
matibat Sep 26, 2023
1a2e618
feat(upload cubit): makes the file upload modal show the correct not …
matibat Sep 26, 2023
405149c
feat(create snapshot cubit): let the snapshot use turbo uploads when …
matibat Sep 27, 2023
144aa4e
test(create snapshot cubit): fixes brokwn tests PE-4687
matibat Sep 27, 2023
abf45c6
feat(create snapshot cubit): corrects typo PE-4687
matibat Sep 27, 2023
51d4d63
Merge pull request #1385 from ardriveapp/PE-4676
matibat Sep 27, 2023
35e0cda
Merge pull request #1387 from ardriveapp/PE-4687
matibat Sep 27, 2023
9b8bfdd
Merge remote-tracking branch 'origin/dev' into PE-3173
matibat Sep 27, 2023
2e886f9
feat(create snapshot dialog): makes the turbo balance be refreshed on…
matibat Sep 28, 2023
853a3dd
feat(pubspec): updates the ardrive-ui dep PE-4694
matibat Sep 28, 2023
3b21e36
chore(create snapshot cubit): cleanup PE-4694
matibat Sep 28, 2023
b5ba8fc
feat(create snapshot cubit): adds a delay to ensure we'll read the co…
matibat Oct 2, 2023
6d84457
feat(dev tools): adds optoins for disabling free uploads, setting a f…
matibat Oct 2, 2023
4d1bf6c
feat(create snapshot cubit): update the state of the button after top…
matibat Oct 2, 2023
cf19253
test(create snapshot cubit): updates broken tests PE-4694
matibat Oct 2, 2023
dfb546d
chore(topup review view): corrects typo in a comment PE-4694
matibat Oct 3, 2023
faf9687
Merge pull request #1394 from ardriveapp/PE-4694
matibat Oct 4, 2023
f34cc58
Merge remote-tracking branch 'origin/dev' into PE-3173
matibat Oct 4, 2023
26ef3cf
chore(create snapshot cubit): removes unnecessary non-null assertion …
matibat Oct 4, 2023
ef0493c
feat(pubspec): version bump of ardrive-ui PE-3173
matibat Oct 4, 2023
2ab486d
Merge pull request #1402 from ardriveapp/PE-3173_dep_version_bump
matibat Oct 4, 2023
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
244 changes: 201 additions & 43 deletions lib/blocs/create_snapshot/create_snapshot_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io' show BytesBuilder;

import 'package:ardrive/authentication/ardrive_auth.dart';
import 'package:ardrive/blocs/constants.dart';
import 'package:ardrive/blocs/profile/profile_cubit.dart';
import 'package:ardrive/blocs/upload/upload_cubit.dart';
import 'package:ardrive/core/upload/cost_calculator.dart';
import 'package:ardrive/entities/entities.dart';
import 'package:ardrive/entities/snapshot_entity.dart';
import 'package:ardrive/entities/string_types.dart';
import 'package:ardrive/models/daos/daos.dart';
import 'package:ardrive/services/arweave/arweave.dart';
import 'package:ardrive/services/config/app_config.dart';
import 'package:ardrive/services/pst/pst.dart';
import 'package:ardrive/turbo/services/payment_service.dart';
import 'package:ardrive/turbo/services/upload_service.dart';
import 'package:ardrive/turbo/turbo.dart';
import 'package:ardrive/turbo/utils/utils.dart';
import 'package:ardrive/utils/html/html_util.dart';
import 'package:ardrive/utils/logger/logger.dart';
import 'package:ardrive/utils/metadata_cache.dart';
import 'package:ardrive/utils/snapshots/height_range.dart';
import 'package:ardrive/utils/snapshots/range.dart';
import 'package:ardrive/utils/snapshots/snapshot_item_to_be_created.dart';
import 'package:arweave/arweave.dart';
import 'package:arweave/utils.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand All @@ -29,10 +35,15 @@ part 'create_snapshot_state.dart';

class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
final ArweaveService _arweave;
final PaymentService _paymentService;
final DriveDao _driveDao;
final ProfileCubit _profileCubit;
final PstService _pst;
final TabVisibilitySingleton _tabVisibility;
final TurboBalanceRetriever turboBalanceRetriever;
final ArDriveAuth auth;
final AppConfig appConfig;
final TurboUploadService turboService;
@visibleForTesting
bool throwOnDataComputingForTesting;
@visibleForTesting
Expand All @@ -44,7 +55,22 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
late String _ownerAddress;
late Range _range;
late int _currentHeight;
late Transaction _preparedTx;
Uint8List? data;
DataItem? _preparedDataItem;
Transaction? _preparedTx;

UploadMethod _uploadMethod = UploadMethod.ar;
UploadCostEstimate _costEstimateAr = UploadCostEstimate.zero();
UploadCostEstimate _costEstimateTurbo = UploadCostEstimate.zero();
bool _hasNoTurboBalance = false;
String _arBalance = '';
String _turboCredits = '';
BigInt _turboBalance = BigInt.zero;
bool _isButtonToUploadEnabled = false;
bool _isTurboUploadPossible = true;
bool _sufficentCreditsBalance = false;
bool _sufficientArBalance = false;
bool _isFreeThanksToTurbo = false;

bool _wasSnapshotDataComputingCanceled = false;

Expand All @@ -53,14 +79,20 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {

CreateSnapshotCubit({
required ArweaveService arweave,
required PaymentService paymentService,
required ProfileCubit profileCubit,
required DriveDao driveDao,
required PstService pst,
required TabVisibilitySingleton tabVisibility,
required this.turboBalanceRetriever,
required this.auth,
required this.appConfig,
required this.turboService,
this.throwOnDataComputingForTesting = false,
this.throwOnSignTxForTesting = false,
this.returnWithoutSigningForTesting = false,
}) : _arweave = arweave,
_paymentService = paymentService,
_profileCubit = profileCubit,
_driveDao = driveDao,
_pst = pst,
Expand Down Expand Up @@ -98,15 +130,15 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {

_setupSnapshotEntityWithBlob(data);

await _prepareAndSignTx(
_snapshotEntity!,
data,
);

final costResult = await _computeCostAndCheckBalance();
if (costResult == null) return;
await _computeCost();
await _computeBalanceEstimate();
_computeIsSufficientBalance();
_computeIsTurboEnabled();
_computeIsFreeThanksToTurbo();

await _emitConfirming(costResult, data.length);
await _emitConfirming(
dataSize: data.length,
);
}

bool _wasCancelled() {
Expand Down Expand Up @@ -180,7 +212,6 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {

Future<void> _prepareAndSignTx(
SnapshotEntity snapshotEntity,
Uint8List data,
) async {
logger.i('About to prepare and sign snapshot transaction');

Expand All @@ -191,10 +222,13 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
));

await prepareTx(isArConnectProfile);
await _pst.addCommunityTipToTx(_preparedTx);
await signTx(isArConnectProfile);

snapshotEntity.txId = _preparedTx.id;
if (_uploadMethod == UploadMethod.ar) {
snapshotEntity.txId = _preparedTx!.id;
} else {
snapshotEntity.txId = _preparedDataItem!.id;
}
}

@visibleForTesting
Expand All @@ -207,13 +241,22 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
'Preparing snapshot transaction with ${isArConnectProfile ? 'ArConnect' : 'JSON wallet'}',
);

_preparedTx = await _arweave.prepareEntityTx(
_snapshotEntity!,
wallet,
null,
// We'll sign it just after adding the tip
skipSignature: true,
);
if (_uploadMethod == UploadMethod.ar) {
_preparedTx = await _arweave.prepareEntityTx(
_snapshotEntity!,
wallet,
null,
// We'll sign it just after adding the tip
skipSignature: true,
);
} else {
_preparedDataItem = await _arweave.prepareEntityDataItem(
_snapshotEntity!,
wallet,
// We'll sign it just after adding the tip
skipSignature: true,
);
}
} catch (e) {
final isTabFocused = _tabVisibility.isTabFocused();
if (isArConnectProfile && !isTabFocused) {
Expand Down Expand Up @@ -249,7 +292,11 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
return;
}

await _preparedTx.sign(wallet);
if (_uploadMethod == UploadMethod.ar) {
await _preparedTx!.sign(wallet);
} else {
await _preparedDataItem!.sign(wallet);
}
} catch (e) {
final isTabFocused = _tabVisibility.isTabFocused();
if (isArConnectProfile && !isTabFocused) {
Expand Down Expand Up @@ -323,39 +370,128 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
_snapshotEntity = snapshotEntity;
}

Future<BigInt?> _computeCostAndCheckBalance() async {
final totalCost = _preparedTx.reward + _preparedTx.quantity;
Future<void> _computeCost() async {
final profileState = _profileCubit.state as ProfileLoggedIn;
final wallet = profileState.wallet;

final profile = _profileCubit.state as ProfileLoggedIn;
final walletBalance = profile.walletBalance;
UploadCostEstimateCalculatorForAR costCalculatorForAr =
UploadCostEstimateCalculatorForAR(
arweaveService: _arweave,
pstService: _pst,
arCostToUsd: ConvertArToUSD(arweave: _arweave),
);

if (walletBalance < totalCost) {
emit(CreateSnapshotInsufficientBalance(
walletBalance: walletBalance.toString(),
arCost: winstonToAr(totalCost),
));
return null;
}
final turboCostCalc = TurboCostCalculator(paymentService: _paymentService);
TurboUploadCostCalculator costCalculatorForTurbo =
TurboUploadCostCalculator(
turboCostCalculator: turboCostCalc,
priceEstimator: TurboPriceEstimator(
wallet: wallet,
paymentService: _paymentService,
costCalculator: turboCostCalc,
),
);

_costEstimateAr = await costCalculatorForAr.calculateCost(
totalSize: _snapshotEntity!.data!.length,
);
_costEstimateTurbo = await costCalculatorForTurbo.calculateCost(
totalSize: _snapshotEntity!.data!.length,
);
}

return totalCost;
Future<void> _computeBalanceEstimate() async {
final profileState = _profileCubit.state as ProfileLoggedIn;
final wallet = profileState.wallet;

final turboBalance =
await turboBalanceRetriever.getBalance(wallet).catchError((e) {
logger.e('Error while retrieving turbo balance', e);
return BigInt.zero;
});

_turboBalance = turboBalance;
_hasNoTurboBalance = turboBalance == BigInt.zero;
_turboCredits = convertCreditsToLiteralString(turboBalance);
_arBalance = convertCreditsToLiteralString(auth.currentUser!.walletBalance);
}

Future<void> _emitConfirming(
BigInt totalCost,
int dataSize,
) async {
final arUploadCost = winstonToAr(totalCost);
void _computeIsTurboEnabled() async {
bool isTurboEnabled = appConfig.useTurboUpload;
_isTurboUploadPossible = isTurboEnabled && _sufficentCreditsBalance;
}

void _computeIsSufficientBalance() {
final profileState = _profileCubit.state as ProfileLoggedIn;

final double? usdUploadCost = await ConvertArToUSD(arweave: _arweave)
.convertForUSD(double.parse(arUploadCost));
bool sufficientBalanceToPayWithAR =
profileState.walletBalance >= _costEstimateAr.totalCost;
bool sufficientBalanceToPayWithTurbo =
_costEstimateTurbo.totalCost <= _turboBalance;

_sufficientArBalance = sufficientBalanceToPayWithAR;
_sufficentCreditsBalance = sufficientBalanceToPayWithTurbo;
}

void _computeIsFreeThanksToTurbo() {
final allowedDataItemSizeForTurbo = appConfig.allowedDataItemSizeForTurbo;
final isFreeThanksToTurbo =
_snapshotEntity!.data!.length <= allowedDataItemSizeForTurbo;
_isFreeThanksToTurbo = isFreeThanksToTurbo;
}

Future<void> _emitConfirming({required int dataSize}) async {
emit(ConfirmingSnapshotCreation(
snapshotSize: dataSize,
arUploadCost: arUploadCost,
usdUploadCost: usdUploadCost,
costEstimateAr: _costEstimateAr,
costEstimateTurbo: _costEstimateTurbo,
hasNoTurboBalance: _hasNoTurboBalance,
isTurboUploadPossible: _isTurboUploadPossible,
arBalance: _arBalance,
turboCredits: _turboCredits,
uploadMethod: _uploadMethod,
isButtonToUploadEnabled: _isButtonToUploadEnabled,
sufficientBalanceToPayWithAr: _sufficientArBalance,
sufficientBalanceToPayWithTurbo: _sufficentCreditsBalance,
isFreeThanksToTurbo: _isFreeThanksToTurbo,
));
}

void setUploadMethod(UploadMethod method) {
logger.d('Upload method set to $method');
_uploadMethod = method;

_refreshIsButtonEnabled();
}

void _refreshIsButtonEnabled() {
_isButtonToUploadEnabled = false;
if (state is ConfirmingSnapshotCreation) {
final stateAsConfirming = state as ConfirmingSnapshotCreation;
logger.d('Sufficient Balance To Pay With AR: $_sufficientArBalance');

if (_uploadMethod == UploadMethod.ar && _sufficientArBalance) {
logger.d('Enabling button for AR payment method');
_isButtonToUploadEnabled = true;
} else if (_uploadMethod == UploadMethod.turbo &&
stateAsConfirming.isTurboUploadPossible &&
_sufficentCreditsBalance) {
logger.d('Enabling button for Turbo payment method');
_isButtonToUploadEnabled = true;
} else if (stateAsConfirming.isFreeThanksToTurbo) {
logger.d('Enabling button for free upload using Turbo');
_isButtonToUploadEnabled = true;
} else {
logger.d('Disabling button');
}

emit(stateAsConfirming.copyWith(
uploadMethod: _uploadMethod,
isButtonToUploadEnabled: _isButtonToUploadEnabled,
));
}
}

Future<Uint8List> _jsonMetadataOfTxId(String txId) async {
final drive =
await _driveDao.driveById(driveId: _driveId).getSingleOrNull();
Expand Down Expand Up @@ -396,10 +532,20 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
return;
}

await _prepareAndSignTx(
_snapshotEntity!,
);

try {
emit(UploadingSnapshot());

await _arweave.postTx(_preparedTx);
if (_uploadMethod == UploadMethod.ar) {
await _arweave.postTx(_preparedTx!);
} else {
await _postTurboDataItem(
dataItem: _preparedDataItem!,
);
}

emit(SnapshotUploadSuccess());
} catch (err, stacktrace) {
Expand All @@ -408,6 +554,18 @@ class CreateSnapshotCubit extends Cubit<CreateSnapshotState> {
}
}

Future<void> _postTurboDataItem({required DataItem dataItem}) async {
final profile = _profileCubit.state as ProfileLoggedIn;
final wallet = profile.wallet;

logger.d('Posting snapshot transaction for drive $_driveId');

await turboService.postDataItem(
dataItem: dataItem,
wallet: wallet,
);
}

void cancelSnapshotCreation() {
logger.i('User cancelled the snapshot creation');

Expand Down
Loading