signTransactionsBundle method
Implementation
@SquadronMethod()
@txSignedBundleMarshaler
Future<TxSignedBundle> signTransactionsBundle(
@walletMarshaler CardanoWallet wallet,
@txSigningBundleMarshaler TxSigningBundle bundle,
int deriveMaxAddressCount,
) async {
final txs = bundle.txsData;
final HdWallet hdWallet = (wallet as CardanoWalletImpl).hdWallet;
final networkId = wallet.networkId;
final List<Set<Bip32KeyPair>> signAddressesAll = List.generate(txs.length, (index) => {});
final List<Set<String>> modifiableSpendCredentialsAll = List.generate(
txs.length,
(index) => Set.of(
txs[index] //
.signingAddressesRequired
.map((e) => CardanoAddress.fromBech32OrBase58(e).credentials),
),
);
final modifiablePubKeyNativeScriptCredentialsAll = List.generate(
txs.length,
(index) => _credsFromNativeScripts(txs[index].tx.witnessSet.nativeScripts).toSet(),
);
final modifiableExtraSignersCredentialsAll = List.generate(
txs.length,
(index) => Set.of(txs[index].tx.body.requiredSigners?.signersBytes.map((e) => e.hexEncode()) ?? <String>[]),
);
// check for stake credentials/full hex stake address ; remove if we find any
final extraSignersRequestedStakeAddressAll = modifiableExtraSignersCredentialsAll
.map(
(e) => e.removeFirstWhere((element) => element.contains(wallet.stakeAddress.credentials)),
)
.toList(growable: false);
final pubKeyNativeScriptRequestedStakeAddressAll = modifiablePubKeyNativeScriptCredentialsAll
.map(
(e) => e.removeFirstWhere((element) => element.contains(wallet.stakeAddress.credentials)),
)
.toList(growable: false);
final hasCertsForWalletStakeKeyAll = txs
.map((e) => e.tx)
.map((tx) => tx.body.certs.requiresStakeSignature(wallet.stakeAddress.credentialsBytes))
.toList(growable: false);
final hasCertsForColdKeyAll = txs
.map((e) => e.tx)
.map((tx) => tx.body.certs.requiresCommitteeColdSignature(
wallet.constitutionalCommiteeCold.value.credentialsBytes,
))
.toList(growable: false);
final hasCertsOrVotesForDRepAll = txs
.map((e) => e.tx)
.map(
(tx) =>
tx.body.certs.requiresDrepSignature(wallet.drepId.value.credentialsBytes) ||
tx.body.votingProcedures.requiresDrepSignature(wallet.drepId.value.credentialsBytes),
)
.toList(growable: false);
final hasCommitteeHotVotesAll = txs
.map((e) => e.tx)
.map(
(tx) => tx.body.votingProcedures.requiresCommitteeHotSignature(
wallet.constitutionalCommiteeHot.value.credentialsBytes,
),
)
.toList(growable: false);
final hasWithdrawalForWalletStakeKeyAll = txs
.map((e) => e.tx)
.map(
(tx) =>
tx.body.withdrawals?.any(
(element) => element.stakeAddressBech32.bech32ToHex().endsWith(wallet.stakeAddress.credentials),
) ??
false,
)
.toList(growable: false);
final deriveAddressWorker = WalletTasksWorkerPool(
concurrencySettings: const ConcurrencySettings(maxParallel: 2, maxWorkers: 2, minWorkers: 2),
);
try {
await deriveAddressWorker.start();
for (int i = 0; i < deriveMaxAddressCount; i++) {
final paymentAddrForIndexFuture = deriveAddressWorker.execute(
(worker) => worker.deriveAddressKit(wallet.hdWallet, networkId, i, Bip32KeyRole.payment),
);
final changeAddrForIndexFuture = deriveAddressWorker.execute(
(worker) => worker.deriveAddressKit(wallet.hdWallet, networkId, i, Bip32KeyRole.change),
);
final paymentAddrForIndex = await paymentAddrForIndexFuture;
final changeAddrForIndex = await changeAddrForIndexFuture;
final hexPaymentAddrForIndex = paymentAddrForIndex.address.hexEncoded;
final hexChangeAddrForIndex = changeAddrForIndex.address.hexEncoded;
final bech32PaymentCredsForIndex = paymentAddrForIndex.address.credentials;
final bech32ChangeCredsForIndex = changeAddrForIndex.address.credentials;
modifiableSpendCredentialsAll.forEachIndexed((index, e) {
if (e.remove(bech32PaymentCredsForIndex)) {
signAddressesAll[index].add(paymentAddrForIndex);
}
if (e.remove(bech32ChangeCredsForIndex)) {
signAddressesAll[index].add(changeAddrForIndex);
}
});
modifiableExtraSignersCredentialsAll.forEachIndexed((index, e) {
if (e.remove(bech32PaymentCredsForIndex)) {
signAddressesAll[index].add(paymentAddrForIndex);
}
if (e.remove(bech32ChangeCredsForIndex)) {
signAddressesAll[index].add(changeAddrForIndex);
}
});
modifiablePubKeyNativeScriptCredentialsAll.forEachIndexed((index, e) {
if (e.removeFirstWhere(hexPaymentAddrForIndex.contains)) {
signAddressesAll[index].add(paymentAddrForIndex);
}
if (e.removeFirstWhere(hexChangeAddrForIndex.contains)) {
signAddressesAll[index].add(changeAddrForIndex);
}
});
if (modifiableSpendCredentialsAll.every((e) => e.isEmpty) &&
modifiableExtraSignersCredentialsAll.every((e) => e.isEmpty) &&
modifiablePubKeyNativeScriptCredentialsAll.every((e) => e.isEmpty)) {
break;
}
}
} finally {
deriveAddressWorker.stop();
}
if (modifiableSpendCredentialsAll.flattened.isNotEmpty) {
throw SigningAddressNotFoundException(
missingAddresses: modifiableSpendCredentialsAll.flattened.toSet(),
searchedAddressesCount: deriveMaxAddressCount,
);
}
final needsStakeAddrSigningAll = List.generate(
txs.length,
(index) =>
hasCertsForWalletStakeKeyAll[index] ||
hasWithdrawalForWalletStakeKeyAll[index] ||
extraSignersRequestedStakeAddressAll[index] ||
pubKeyNativeScriptRequestedStakeAddressAll[index],
);
needsStakeAddrSigningAll.forEachIndexed((index, needsStakeAddrSigning) {
if (needsStakeAddrSigning) {
signAddressesAll[index].add(hdWallet.stakeKeys.value);
}
});
final needsConstitutionalCommitteeColdSignAll = List.generate(
txs.length,
(index) => hasCertsForColdKeyAll[index],
);
needsConstitutionalCommitteeColdSignAll.forEachIndexed((index, needsCommitteeColdSigning) {
if (needsCommitteeColdSigning) {
signAddressesAll[index].add(hdWallet.constitutionalCommitteeColdKeys.value);
}
});
final needsConstitutionalCommitteeHotSignAll = List.generate(
txs.length,
(index) => hasCommitteeHotVotesAll[index],
);
needsConstitutionalCommitteeHotSignAll.forEachIndexed((index, needsCommitteeHotSigning) {
if (needsCommitteeHotSigning) {
signAddressesAll[index].add(hdWallet.constitutionalCommitteeHotKeys.value);
}
});
final needsDRepIdSignAll = List.generate(
txs.length,
(index) => hasCertsOrVotesForDRepAll[index],
);
needsDRepIdSignAll.forEachIndexed((index, needsDRepIdSigning) {
if (needsDRepIdSigning) {
signAddressesAll[index].add(hdWallet.drepCredentialKeys.value);
}
});
final txsAndSignatures = txs.mapIndexed((index, e) {
final tx = e.tx;
final Uint8List bodyHash = tx.body.blake2bHash256Hex().hexDecode();
final vKeyWitnesses = signAddressesAll[index].map((signAddr) {
final signedMessage = signAddr.signingKey.sign(bodyHash);
final witness = WitnessVKey(
vkey: signAddr.verifyKey.rawKey.toUint8List(),
signature: signedMessage.signature.toUint8List(),
);
return witness;
});
final witness = WitnessSet(
ivkeyWitnesses: ListWithCborType(
vKeyWitnesses.toList(),
CborLengthType.auto,
null,
),
);
return TxAndSignature(
tx: tx,
txDiff: e.txDiff,
utxosBeforeTx: e.utxosBeforeTx,
signingAddressesRequired: e.signingAddressesRequired,
nweSignatures: witness,
);
});
return TxSignedBundle(
txsData: txsAndSignatures.toList(growable: false),
totalDiff: bundle.totalDiff,
networkId: bundle.networkId,
receiveAddressBech32: bundle.receiveAddressBech32,
);
}