sign method
Implementation
@override
GsplTxData sign() {
// Deserialize and identify transaction information, get readable transaction structure
final tx = btc.Transaction.fromHex(txData.hex);
final inputAddress = wallet.publicKeyToAddress(wallet.publicKey);
final payments = txData.toJson()['payments'];
if (payments == null || payments.isEmpty) {
throw Exception('No valid output found in transaction outputs');
}
final prevOutScript = btc.Address.addressToOutputScript(inputAddress, networkType)!;
// BIP341 sighash commits to every spent output's scriptPubKey and
// amount, so gather them for all inputs up front (GSPL inputs all
// belong to the same wallet address, so the script is shared).
final allPrevScripts = <Uint8List>[];
final allValues = <int>[];
for (final input in txData.inputs) {
if (input.amount == null) throw Exception('Input amount required for sigHash');
allPrevScripts.add(prevOutScript);
allValues.add(input.amount!);
}
// Iterate through inputs, use wallet.sign to sign sigHash
final List<GsplItem> signedInputs = [];
for (int i = 0; i < txData.inputs.length; i++) {
final input = txData.inputs[i];
if (input.path == null) throw Exception('Input path cannot be null');
if (input.amount == null) throw Exception('Input amount required for sigHash');
final hashType = _hashTypeFor(input);
final sigHash =
_sigHashFor(tx, i, prevOutScript, allPrevScripts, allValues, hashType);
String sigResult;
final sigHashHex = dynamicToString(sigHash);
if (isTaprootInput) {
sigResult = (wallet as LtcCoin).signWithHashType(sigHashHex, hashType);
} else if (isLtc) {
sigResult = (wallet as LtcCoin).signWithHashType(sigHashHex, hashType);
} else if (isDoge) {
sigResult = (wallet as DogeCoin).signWithHashType(sigHashHex, hashType);
} else if (isBch) {
sigResult = (wallet as BchCoin).signWithHashType(sigHashHex, hashType);
} else {
sigResult = wallet.sign(sigHashHex);
}
final signatureBytes = dynamicToUint8List(sigResult);
if (isTaprootInput) {
if (signatureBytes.length != 64) {
throw StateError(
'Taproot signer must return a raw 64-byte signature',
);
}
if (hashType != btc.SIGHASH_DEFAULT &&
!_isValidTaprootHashType(hashType)) {
throw ArgumentError('Invalid Taproot sighash type: $hashType');
}
}
// Construct new GsplItem to replace
signedInputs.add(GsplItem(
path: input.path,
amount: input.amount,
address: inputAddress,
signHashType: input.signHashType,
signature: signatureBytes,
));
}
final transactionSigned = btc.Transaction.fromHex(txData.hex);
for (int i = 0; i < signedInputs.length; i++) {
final sig = signedInputs[i].signature;
if (sig == null) {
throw Exception('Missing signature for input $i');
}
if (isTaprootInput) {
// P2TR key-path spend: the Schnorr signature goes in the witness
// stack, and scriptSig stays empty.
final hashType = signedInputs[i].signHashType ?? btc.SIGHASH_DEFAULT;
final witnessSig = hashType == btc.SIGHASH_DEFAULT
? sig
: Uint8List.fromList([...sig, hashType]);
transactionSigned.ins[i].witness = [witnessSig];
transactionSigned.ins[i].script = btc.EMPTY_SCRIPT;
} else {
final pubkey = wallet.publicKey;
final scriptSig = bscript.compile([sig, pubkey]);
transactionSigned.ins[i].script = scriptSig;
}
}
final signedHex = transactionSigned.toHex();
txData.hex = signedHex;
txData.inputs = signedInputs;
txData.isSigned = true;
txData.message = signedHex;
txData.signature = '';
return txData;
}