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

withdrawal bundles: fetch bundle info #32

Merged
merged 1 commit into from
Nov 7, 2023
Merged
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
3 changes: 2 additions & 1 deletion lib/pages/tabs/withdrawal_bundle_tab_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class WithdrawalBundleTabPage extends StatelessWidget {
title: 'Current withdrawal bundle',
endWidget: SailText.primary12(
[
'ID: ${viewModel.currentBundle?.hash}',
'Created at block height ${viewModel.currentBundle?.blockHeight}',
'${viewModel.votes}/${viewModel.votesRequired} votes',
'${viewModel.currentBundle?.bundleSize}/${viewModel.currentBundle?.maxBundleSize} vbytes',
Expand Down Expand Up @@ -157,7 +158,7 @@ class _WithdrawalViewState extends State<WithdrawalView> {
child: SailSVG.icon(SailSVGAsset.iconPending, width: 13),
),
copyable: false,
label: 'TODO',
label: widget.withdrawal.status,
value: extractTitle(widget.withdrawal),
),
],
Expand Down
9 changes: 8 additions & 1 deletion lib/rpc/rpc_mainchain.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,14 @@ class MainchainRPCLive extends MainchainRPC {

@override
Future<int> getWithdrawalBundleWorkScore(int sidechain, String hash) async {
return await _client?.call('getworkscore', [sidechain, hash]);
try {
final workscore = await _client?.call('getworkscore', [sidechain, hash]);
return workscore;
} on RPCException {
// This exception can be thrown if the bundle hasn't been added to the
// Sidechain DB yet. Avoid erroring here, and instead return 0.
return 0;
}
}

@override
Expand Down
54 changes: 54 additions & 0 deletions lib/rpc/rpc_sidechain.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,26 @@ class SidechainRPCLive extends SidechainRPC {
final decoded = await _client?.call('decoderawtransaction', [rawWithdrawalBundle]);
final tx = RawTransaction.fromJson(decoded);

final info = await _client?.call(
'getwithdrawalbundleinfo',
[tx.hash],
);

final withdrawalIDs = info['withdrawals'] as List<dynamic>;

final withdrawals = await Future.wait(
withdrawalIDs.map(
(id) => _client!.call(
'getwithdrawal',
[id],
).then((json) => Withdrawal.fromJson(json)),
),
);

return WithdrawalBundle.fromRawTransaction(
tx,
BundleInfo.fromJson(info),
withdrawals,
);
}

Expand All @@ -249,6 +267,9 @@ class SidechainRPCLive extends SidechainRPC {
mainchainFeesSatoshi: withdrawal['amountmainchainfee'],
amountSatoshi: withdrawal['amount'],
address: withdrawal['destination'],
hashBlindTx: '', // TODO
refundDestination: '', // TODO
status: '', // TODO
),
)
.toList(),
Expand All @@ -257,6 +278,7 @@ class SidechainRPCLive extends SidechainRPC {
}

class RPCError {
static const errMisc = -3;
static const errNoWithdrawalBundle = -100;
static const errWithdrawalNotFound = -101;
}
Expand Down Expand Up @@ -415,3 +437,35 @@ String? ifNonEmpty(String input) {

return input;
}

class BundleInfo {
final int amountSatoshi;
final int feesSatoshi;
final int weight;
final int height;

BundleInfo({
required this.amountSatoshi,
required this.feesSatoshi,
required this.weight,
required this.height,
});

factory BundleInfo.fromJson(Map<String, dynamic> json) {
return BundleInfo(
amountSatoshi: json['amount'],
feesSatoshi: json['fees'],
weight: json['weight'],
height: json['height'],
);
}

Map<String, dynamic> toJson() {
return {
'amount': amountSatoshi,
'fees': feesSatoshi,
'weight': weight,
'height': height,
};
}
}
47 changes: 33 additions & 14 deletions lib/rpc/rpc_withdrawal_bundle.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:sidesail/rpc/rpc_rawtx.dart';
import 'package:sidesail/rpc/rpc_sidechain.dart';

class WithdrawalBundle {
WithdrawalBundle({
Expand All @@ -10,22 +11,14 @@ class WithdrawalBundle {

factory WithdrawalBundle.fromRawTransaction(
RawTransaction tx,
BundleInfo info,
List<Withdrawal> withdrawals,
) =>
WithdrawalBundle(
hash: tx.hash,
bundleSize: tx.size * 4,
blockHeight: 0, // TODO: how to get this
withdrawals: tx.vout
// filter out OP_RETURN
.where((out) => out.scriptPubKey.type != 'nulldata')
.map(
(out) => Withdrawal(
mainchainFeesSatoshi: 0, // TODO: how to get this
amountSatoshi: (out.value * 100 * 1000 * 1000).toInt(),
address: out.scriptPubKey.addresses.first,
),
)
.toList(),
bundleSize: info.weight,
blockHeight: info.height,
withdrawals: withdrawals,
);

final String hash;
Expand Down Expand Up @@ -56,9 +49,35 @@ class Withdrawal {
required this.mainchainFeesSatoshi,
required this.amountSatoshi,
required this.address,
required this.hashBlindTx,
required this.refundDestination,
required this.status,
});

final int mainchainFeesSatoshi; // TODO: how to obtain?
final int mainchainFeesSatoshi;
final int amountSatoshi;
final String address;
final String hashBlindTx;
final String refundDestination;

// Directly from RPC interface.
final String status;

factory Withdrawal.fromJson(Map<String, dynamic> json) => Withdrawal(
address: json['destination'],
refundDestination: json['refunddestination'],
amountSatoshi: json['amount'],
mainchainFeesSatoshi: json['amountmainchainfee'],
status: json['status'],
hashBlindTx: json['hashblindtx'],
);

Map<String, dynamic> toJson() => {
'destination': address,
'refunddestination': refundDestination,
'amount': amountSatoshi,
'amountmainchainfee': mainchainFeesSatoshi,
'status': status,
'hashblindtx': hashBlindTx,
};
}