mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
* Shuffle change output on hardware wallet sends Hardware wallet BTC/LTC sends passed outputs to the PSBT/device in the given order (change last), a position fingerprint. The outputOrdering param was plumbed but ignored. Add orderOutputs() and apply it in both buildHardwareWalletTransaction paths; set the send call site to shuffle. Refs #3376 (the software send + RBF paths were covered by #3420). * Shuffle change output on Bitcoin Cash sends The BCH send path built via ForkedTransactionBuilder with outputOrdering: none, leaving change deterministically last. The builder shuffles natively and change is found by isChange, not position, so flip it to shuffle (matches the BTC software path from #3420). Refs #3376. * Update cw_bitcoin/lib/electrum_wallet.dart Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
53 lines
2.0 KiB
Dart
53 lines
2.0 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:bitcoin_base/bitcoin_base.dart';
|
|
import 'package:cw_bitcoin/output_ordering.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
group('orderOutputs', () {
|
|
test('none preserves the given order', () {
|
|
final outputs = [0, 1, 2, 3, 4];
|
|
expect(orderOutputs(outputs, BitcoinOrdering.none), outputs);
|
|
});
|
|
|
|
test('does not mutate the input list', () {
|
|
final outputs = [0, 1, 2, 3, 4];
|
|
orderOutputs(outputs, BitcoinOrdering.shuffle, rng: Random(1));
|
|
expect(outputs, [0, 1, 2, 3, 4]);
|
|
});
|
|
|
|
test('returns a new list instance', () {
|
|
final outputs = [0, 1, 2];
|
|
expect(identical(orderOutputs(outputs, BitcoinOrdering.none), outputs), isFalse);
|
|
});
|
|
|
|
test('shuffle preserves the multiset of outputs', () {
|
|
final outputs = [0, 1, 2, 3, 4, 5, 6, 7];
|
|
final shuffled = orderOutputs(outputs, BitcoinOrdering.shuffle, rng: Random(7));
|
|
expect(shuffled.length, outputs.length);
|
|
expect(shuffled.toSet(), outputs.toSet());
|
|
});
|
|
|
|
test('shuffle is deterministic for a given seed', () {
|
|
final outputs = [0, 1, 2, 3, 4, 5];
|
|
final a = orderOutputs(outputs, BitcoinOrdering.shuffle, rng: Random(42));
|
|
final b = orderOutputs(outputs, BitcoinOrdering.shuffle, rng: Random(42));
|
|
expect(a, b);
|
|
});
|
|
|
|
test('shuffle does not keep the change output deterministically last', () {
|
|
// Last element (99) models the change output, appended last today.
|
|
final outputs = [0, 1, 2, 3, 99];
|
|
final changePositions = <int>{};
|
|
for (var seed = 0; seed < 50; seed++) {
|
|
final shuffled = orderOutputs(outputs, BitcoinOrdering.shuffle, rng: Random(seed));
|
|
changePositions.add(shuffled.indexOf(99));
|
|
}
|
|
// Across seeds the change lands in more than one position, and not always last.
|
|
expect(changePositions.length, greaterThan(1));
|
|
expect(changePositions, isNot(equals({outputs.length - 1})));
|
|
});
|
|
});
|
|
}
|