WIP particl set tx version to 160 and strip trailing 00s

This commit is contained in:
sneurlax 2024-01-11 17:37:27 -06:00
parent 52477e124f
commit 2cbca50d52

View file

@ -1,13 +1,17 @@
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
import 'package:isar/isar.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/transaction.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart';
import 'package:stackwallet/models/signing_data.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/extensions/impl/uint8_list.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/wallets/crypto_currency/coins/particl.dart';
import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart';
import 'package:stackwallet/wallets/models/tx_data.dart';
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart';
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart';
@ -282,4 +286,157 @@ class ParticlWallet extends Bip39HDWallet
await mainDB.updateOrPutTransactionV2s(txns);
}
/// Builds and signs a transaction.
@override
Future<TxData> buildTransaction({
required TxData txData,
required List<SigningData> utxoSigningData,
}) async {
Logging.instance.log("Starting Particl buildTransaction ----------",
level: LogLevel.Info);
// TODO: use coinlib
final txb = bitcoindart.TransactionBuilder(
network: bitcoindart.NetworkType(
messagePrefix: cryptoCurrency.networkParams.messagePrefix,
bech32: cryptoCurrency.networkParams.bech32Hrp,
bip32: bitcoindart.Bip32Type(
public: cryptoCurrency.networkParams.pubHDPrefix,
private: cryptoCurrency.networkParams.privHDPrefix,
),
pubKeyHash: cryptoCurrency.networkParams.p2pkhPrefix,
scriptHash: cryptoCurrency.networkParams.p2shPrefix,
wif: cryptoCurrency.networkParams.wifPrefix,
),
);
const version = 160; // buildTransaction overridden for Particl to set this.
// TODO: [prio=low] refactor overridden buildTransaction to use eg. cryptocurrency.networkParams.txVersion.
txb.setVersion(version);
// Temp tx data for GUI while waiting for real tx from server.
final List<InputV2> tempInputs = [];
final List<OutputV2> tempOutputs = [];
// Add inputs.
for (var i = 0; i < utxoSigningData.length; i++) {
final txid = utxoSigningData[i].utxo.txid;
txb.addInput(
txid,
utxoSigningData[i].utxo.vout,
null,
utxoSigningData[i].output!,
cryptoCurrency.networkParams.bech32Hrp,
);
tempInputs.add(
InputV2.isarCantDoRequiredInDefaultConstructor(
scriptSigHex: txb.inputs.first.script?.toHex,
sequence: 0xffffffff - 1,
outpoint: OutpointV2.isarCantDoRequiredInDefaultConstructor(
txid: utxoSigningData[i].utxo.txid,
vout: utxoSigningData[i].utxo.vout,
),
addresses: utxoSigningData[i].utxo.address == null
? []
: [utxoSigningData[i].utxo.address!],
valueStringSats: utxoSigningData[i].utxo.value.toString(),
witness: null,
innerRedeemScriptAsm: null,
coinbase: null,
walletOwns: true,
),
);
}
// Add outputs.
for (var i = 0; i < txData.recipients!.length; i++) {
txb.addOutput(
txData.recipients![i].address,
txData.recipients![i].amount.raw.toInt(),
cryptoCurrency.networkParams.bech32Hrp,
);
tempOutputs.add(
OutputV2.isarCantDoRequiredInDefaultConstructor(
scriptPubKeyHex: "000000",
valueStringSats: txData.recipients![i].amount.raw.toString(),
addresses: [
txData.recipients![i].address.toString(),
],
walletOwns: (await mainDB.isar.addresses
.where()
.walletIdEqualTo(walletId)
.filter()
.valueEqualTo(txData.recipients![i].address)
.valueProperty()
.findFirst()) !=
null,
),
);
}
// Sign.
try {
for (var i = 0; i < utxoSigningData.length; i++) {
txb.sign(
vin: i,
keyPair: utxoSigningData[i].keyPair!,
witnessValue: utxoSigningData[i].utxo.value,
redeemScript: utxoSigningData[i].redeemScript,
overridePrefix: cryptoCurrency.networkParams.bech32Hrp,
);
}
} catch (e, s) {
Logging.instance.log("Caught exception while signing transaction: $e\n$s",
level: LogLevel.Error);
rethrow;
}
final builtTx = txb.build(cryptoCurrency.networkParams.bech32Hrp);
final vSize = builtTx.virtualSize();
// Strip trailing 0x00 bytes from hex.
String hexString = builtTx.toHex();
// Ensure the string has an even length.
if (hexString.length % 2 != 0) {
Logging.instance.log("Hex string has odd length, which is unexpected.",
level: LogLevel.Error);
throw Exception("Invalid hex string length.");
}
// Strip up trailing '00' bytes.
int numStrips = 0;
int maxStrips = 3; // Strip up to 3 (match previous particl_wallet).
while (hexString.endsWith('00') && hexString.length > 2) {
hexString = hexString.substring(0, hexString.length - 2);
numStrips++;
if (numStrips >= maxStrips) {
break;
}
}
return txData.copyWith(
raw: hexString,
vSize: vSize,
tempTx: TransactionV2(
walletId: walletId,
blockHash: null,
hash: builtTx.getId(),
txid: builtTx.getId(),
height: null,
timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000,
inputs: List.unmodifiable(tempInputs),
outputs: List.unmodifiable(tempOutputs),
version: version,
type: tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e)
? TransactionType.sentToSelf
: TransactionType.outgoing,
subType: TransactionSubType.none,
otherData: null,
),
);
}
}