Skip to content

Commit

Permalink
withdrawal bundles: fetch bundle info
Browse files Browse the repository at this point in the history
  • Loading branch information
torkelrogstad committed Nov 7, 2023
1 parent 7103fb2 commit c3cff5c
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 16 deletions.
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,
};
}

0 comments on commit c3cff5c

Please sign in to comment.