mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
* Initial Payjoin * Initial Payjoin * More payjoin stuff * Minor fixes * Minor fixes * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix minor bug causes by data inconsistency in the btc utxos * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Initial Payjoin * Initial Payjoin * More payjoin stuff * Minor fixes * Minor fixes * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix minor bug causes by data inconsistency in the btc utxos * Minor cleanup * Minor cleanup * Minor cleanup * Minor cleanup * Fix Rebase issues * Move PJ Receiver to isolate * Add Payjoin Setting * Payjoin Sender are now isolated * Added Payjoin sessions to tx overview. Fix Fee issue with payjoin * Clean up code * Fix taproot for payjoin * Fix CI Errors * Add Payjoin UI elements and details page * Add Payjoin UI elements and details page * Fix Translations * feat: Detect Payjoin URIs in pasted text and show to the User sending Payjoin * feat: rename pjUri to payjoinURI for more code clarity * Update res/values/strings_pl.arb Co-authored-by: cyan <cyjan@mrcyjanek.net> * Update cw_bitcoin/lib/payjoin/manager.dart Co-authored-by: cyan <cyjan@mrcyjanek.net> * Update cw_bitcoin/lib/payjoin/manager.dart Co-authored-by: cyan <cyjan@mrcyjanek.net> * feat: Disable Payjoin per default * feat: Disable Payjoin fully if disabled or no Inputs available * feat: Resume Payjoin if app comes back to foreground * chore: Revert overly aggressive code formats * feat: show correct Payjoin amount for receivers * feat: Improved payjoin status * feat: Show payjoin errors on payjoin details screen * deps: update flutter to 3.27.4 * feat: Revert localisations * bug: Remove duplicate transaction id on payjoin details * style: remove double await in payjoin sender * refactor(cw_bitcoin): Refactor method signatures and convert constructor to factory * refactor(cw_bitcoin): Refactor wallet service and PSBT signer for cleaner code Removed unnecessary `CakeHive` dependency and refactored `BitcoinWallet` initialization to use `payjoinSessionSource`. Improved code readability in `PsbtSigner` by reformatting lines and simplifying constructor methods for `UtxoWithPrivateKey`. * fix: Resume Payjoin Sessions and load PJUri after sleep * feat: Add "Copy Payjoin URL button" to receive screen * fix: Add "Payjoin enabled"-Box below QR Code on the receive screen * fix: Set payjoin_enabled color to black independent of the theme * refactor: Payjoin session management and cleanup unused code. --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com> Co-authored-by: cyan <cyjan@mrcyjanek.net>
143 lines
5.3 KiB
Dart
143 lines
5.3 KiB
Dart
import "dart:typed_data";
|
|
|
|
import "package:ledger_bitcoin/src/psbt/constants.dart";
|
|
import "package:ledger_bitcoin/src/psbt/psbtv2.dart";
|
|
import "package:ledger_bitcoin/src/utils/buffer_writer.dart";
|
|
|
|
/// This roughly implements the "input finalizer" role of BIP370 (PSBTv2
|
|
/// https://github.com/bitcoin/bips/blob/master/bip-0370.mediawiki). However
|
|
/// the role is documented in BIP174 (PSBTv0
|
|
/// https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki).
|
|
///
|
|
/// Verify that all inputs have a signature, and set inputFinalScriptwitness
|
|
/// and/or inputFinalScriptSig depending on the type of the spent outputs. Clean
|
|
/// fields that aren't useful anymore, partial signatures, redeem script and
|
|
/// derivation paths.
|
|
///
|
|
/// @param psbt The psbt with all signatures added as partial sigs, either
|
|
/// through PSBT_IN_PARTIAL_SIG or PSBT_IN_TAP_KEY_SIG
|
|
extension InputFinalizer on PsbtV2 {
|
|
void finalizeV0() {
|
|
|
|
// First check that each input has a signature
|
|
for (var i = 0; i < getGlobalInputCount(); i++) {
|
|
if (_isFinalized(i)) continue;
|
|
|
|
final legacyPubkeys = getInputKeyDatas(i, PSBTIn.partialSig);
|
|
final taprootSig = getInputTapKeySig(i);
|
|
if (legacyPubkeys.isEmpty && taprootSig == null) {
|
|
continue;
|
|
// throw Exception('No signature for input $i present');
|
|
}
|
|
if (legacyPubkeys.isNotEmpty) {
|
|
if (legacyPubkeys.length > 1) {
|
|
throw Exception(
|
|
'Expected exactly one signature, got ${legacyPubkeys.length}');
|
|
}
|
|
if (taprootSig != null) {
|
|
throw Exception('Both taproot and non-taproot signatures present.');
|
|
}
|
|
|
|
final isSegwitV0 = getInputWitnessUtxo(i) != null;
|
|
final redeemScript = getInputRedeemScript(i);
|
|
final isWrappedSegwit = redeemScript != null;
|
|
final signature = getInputPartialSig(i, legacyPubkeys[0]);
|
|
if (signature == null) {
|
|
throw Exception('Expected partial signature for input $i');
|
|
}
|
|
if (isSegwitV0) {
|
|
final witnessBuf = BufferWriter()
|
|
..writeVarInt(2)
|
|
..writeVarInt(signature.length)
|
|
..writeSlice(signature)
|
|
..writeVarInt(legacyPubkeys[0].length)
|
|
..writeSlice(legacyPubkeys[0]);
|
|
setInputFinalScriptwitness(i, witnessBuf.buffer());
|
|
if (isWrappedSegwit) {
|
|
if (redeemScript.isEmpty) {
|
|
throw Exception(
|
|
"Expected non-empty redeemscript. Can't finalize intput $i");
|
|
}
|
|
final scriptSigBuf = BufferWriter()
|
|
..writeUInt8(redeemScript.length) // Push redeemScript length
|
|
..writeSlice(redeemScript);
|
|
setInputFinalScriptsig(i, scriptSigBuf.buffer());
|
|
}
|
|
} else {
|
|
// Legacy input
|
|
final scriptSig = BufferWriter();
|
|
_writePush(scriptSig, signature);
|
|
_writePush(scriptSig, legacyPubkeys[0]);
|
|
setInputFinalScriptsig(i, scriptSig.buffer());
|
|
}
|
|
} else {
|
|
// Taproot input
|
|
final signature = getInputTapKeySig(i);
|
|
if (signature == null) {
|
|
throw Exception("No taproot signature found");
|
|
}
|
|
if (signature.length != 64 && signature.length != 65) {
|
|
throw Exception("Unexpected length of schnorr signature.");
|
|
}
|
|
final witnessBuf = BufferWriter()
|
|
..writeVarInt(1)
|
|
..writeVarSlice(signature);
|
|
setInputFinalScriptwitness(i, witnessBuf.buffer());
|
|
}
|
|
clearFinalizedInput(i);
|
|
}
|
|
}
|
|
|
|
/// Deletes fields that are no longer neccesary from the psbt.
|
|
///
|
|
/// Note, the spec doesn't say anything about removing ouput fields
|
|
/// like PSBT_OUT_BIP32_DERIVATION_PATH and others, so we keep them
|
|
/// without actually knowing why. I think we should remove them too.
|
|
void clearFinalizedInput(int inputIndex) {
|
|
final keyTypes = [
|
|
PSBTIn.bip32Derivation,
|
|
PSBTIn.partialSig,
|
|
PSBTIn.tapBip32Derivation,
|
|
PSBTIn.tapKeySig,
|
|
];
|
|
final witnessUtxoAvailable = getInputWitnessUtxo(inputIndex) != null;
|
|
final nonWitnessUtxoAvailable = getInputNonWitnessUtxo(inputIndex) != null;
|
|
if (witnessUtxoAvailable && nonWitnessUtxoAvailable) {
|
|
// Remove NON_WITNESS_UTXO for segwit v0 as it's only needed while signing.
|
|
// Segwit v1 doesn't have NON_WITNESS_UTXO set.
|
|
// See https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#cite_note-7
|
|
keyTypes.add(PSBTIn.nonWitnessUTXO);
|
|
}
|
|
deleteInputEntries(inputIndex, keyTypes);
|
|
}
|
|
|
|
/// Writes a script push operation to buf, which looks different
|
|
/// depending on the size of the data. See
|
|
/// https://en.bitcoin.it/wiki/Script#finalants
|
|
///
|
|
/// [buf] the BufferWriter to write to
|
|
/// [data] the Buffer to be pushed.
|
|
void _writePush(BufferWriter buf, Uint8List data) {
|
|
if (data.length <= 75) {
|
|
buf.writeUInt8(data.length);
|
|
} else if (data.length <= 256) {
|
|
buf.writeUInt8(76);
|
|
buf.writeUInt8(data.length);
|
|
} else if (data.length <= 256 * 256) {
|
|
buf.writeUInt8(77);
|
|
final b = ByteData(2)..setUint16(0, data.length, Endian.little);
|
|
buf.writeSlice(b.buffer.asUint8List());
|
|
}
|
|
buf.writeSlice(data);
|
|
}
|
|
|
|
bool _isFinalized(int i) {
|
|
if (getInputFinalScriptsig(i) != null) return true;
|
|
try {
|
|
getInputFinalScriptwitness(i);
|
|
return true;
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|