Skip to content

Commit

Permalink
feat: cip30 signTx should send CBOR in sign transactionWitnesserReq
Browse files Browse the repository at this point in the history
Instead of the only the transaction body.
The witnesser request was missing the auxiliaryData and witness fields.
Plus, it's more reliable to use the CBOR instead of reconstructing it.
  • Loading branch information
mirceahasegan committed Oct 6, 2024
1 parent 3879bf1 commit ed57710
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 19 deletions.
4 changes: 4 additions & 0 deletions packages/core/src/Cardano/types/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PlutusData } from './PlutusData';
import { ProposalProcedure, VotingProcedures } from './Governance';
import { RewardAccount } from '../Address';
import { Script } from './Script';
import { Serialization } from '../..';

/** transaction hash as hex string */
export type TransactionId = OpaqueString<'TransactionId'>;
Expand Down Expand Up @@ -164,3 +165,6 @@ export type TxBodyWithHash = {
hash: TransactionId;
body: TxBody;
};

export const isTxBodyWithHash = (tx: Serialization.TxCBOR | TxBodyWithHash): tx is TxBodyWithHash =>
typeof tx === 'object' && 'hash' in tx && 'body' in tx;
29 changes: 16 additions & 13 deletions packages/wallet/src/Wallets/BaseWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,26 +619,29 @@ export class BaseWallet implements ObservableWallet {
}: FinalizeTxProps): Promise<Cardano.Tx> {
const knownAddresses = await firstValueFrom(this.addresses$);
const dRepPublicKey = await this.governance.getPubDRepKey();
const emptyWitness = { signatures: new Map() };

let transaction: Serialization.Transaction;
if (Cardano.isTxBodyWithHash(tx)) {
// Reconstruct transaction from parts
transaction = new Serialization.Transaction(
bodyCbor ? Serialization.TransactionBody.fromCbor(bodyCbor) : Serialization.TransactionBody.fromCore(tx.body),
Serialization.TransactionWitnessSet.fromCore({ ...emptyWitness, ...witness }),
auxiliaryData ? Serialization.AuxiliaryData.fromCore(auxiliaryData) : undefined
);
if (isValid !== undefined) transaction.setIsValid(isValid);
} else {
// Transaction CBOR is available. Use as is.
transaction = Serialization.Transaction.fromCbor(tx);
}

const context = {
...signingContext,
dRepPublicKey,
knownAddresses,
txInKeyPathMap: await util.createTxInKeyPathMap(tx.body, knownAddresses, this.util)
txInKeyPathMap: await util.createTxInKeyPathMap(transaction.body().toCore(), knownAddresses, this.util)
};

const emptyWitness = { signatures: new Map() };

// The Witnesser takes a serializable transaction. We cant build that from the hash alone, if
// the bodyCbor is available, use that instead of the coreTx type to build the transaction.
const transaction = new Serialization.Transaction(
bodyCbor ? Serialization.TransactionBody.fromCbor(bodyCbor) : Serialization.TransactionBody.fromCore(tx.body),
Serialization.TransactionWitnessSet.fromCore({ ...emptyWitness, ...witness }),
auxiliaryData ? Serialization.AuxiliaryData.fromCore(auxiliaryData) : undefined
);

if (isValid !== undefined) transaction.setIsValid(isValid);

const result = await this.witnesser.witness(transaction, context, signingOptions);

this.#newTransactions.signed$.next(result);
Expand Down
9 changes: 4 additions & 5 deletions packages/wallet/src/cip30.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,10 +465,10 @@ const baseCip30WalletApi = (
},
signTx: async ({ sender }: SenderContext, tx: Cbor, partialSign?: Boolean): Promise<Cbor> => {
const scope = new ManagedFreeableScope();
logger.debug('signTx');
const txDecoded = Serialization.Transaction.fromCbor(Serialization.TxCBOR(tx));
logger.debug('signTx', tx);
const txCbor = Serialization.TxCBOR(tx);
const txDecoded = Serialization.Transaction.fromCbor(txCbor);
const wallet = await firstValueFrom(wallet$);
const hash = txDecoded.getId();
const coreTx = txDecoded.toCore();

const needsForeignSignature = await requiresForeignSignatures(coreTx, wallet);
Expand All @@ -493,9 +493,8 @@ const baseCip30WalletApi = (
witness: { signatures }
} = await signOrCancel(
wallet.finalizeTx({
bodyCbor: txDecoded.body().toCbor(),
signingContext: { sender },
tx: { ...coreTx, hash }
tx: txCbor
}),
confirmationResult,
() => new TxSignError(TxSignErrorCode.UserDeclined, 'user declined signing tx')
Expand Down
7 changes: 6 additions & 1 deletion packages/wallet/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ export interface SyncStatus extends Shutdown {
isSettled$: Observable<boolean>;
}

/**
* If tx is the transaction CBOR, the auxiliary data, witness and isValid properties are ignored.
* If tx is `Cardano.TxBodyWithHash`, the transaction is reconstructed from along with the other
* provided properties.
*/
export type FinalizeTxProps = Omit<TxContext, 'signingContext'> & {
tx: Cardano.TxBodyWithHash;
tx: Cardano.TxBodyWithHash | Serialization.TxCBOR;
bodyCbor?: HexBlob;
signingContext?: Partial<SignTransactionContext>;
};
Expand Down

0 comments on commit ed57710

Please sign in to comment.